Commit ac3d5bfe authored by Luk?? Karas's avatar Luk?? Karas Committed by Mauro Carvalho Chehab

V4L/DVB (11451): gspca - m5602-s5k83a: Add rotation, ctrl cache. Rename some ctrls.

s5k83a sensor mounted on many acer laptops have a swiwel allowing it to be rotated. When the camera is in its rotated state, the image needs to be flipped. The only way to check for if the camera has been flipped is to continously poll a register in the m5602. This patch creates a kernel thread which does this. This patch renames some v4l2 ctrls and finally implements a cache in order to prevent unnecessary sensor reads.
Signed-off-by: default avatarLuk?? Karas <lukas.karas@centrum.cz>
Signed-off-by: default avatarErik Andr?n <erik.andren@gmail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 04f15655
...@@ -80,6 +80,16 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data) ...@@ -80,6 +80,16 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
} }
int m5602_wait_for_i2c(struct sd *sd)
{
int err;
u8 data;
do {
err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data);
} while ((data & I2C_BUSY) && !err);
return (err < 0) ? err : 0;
}
int m5602_read_sensor(struct sd *sd, const u8 address, int m5602_read_sensor(struct sd *sd, const u8 address,
u8 *i2c_data, const u8 len) u8 *i2c_data, const u8 len)
{ {
...@@ -88,9 +98,7 @@ int m5602_read_sensor(struct sd *sd, const u8 address, ...@@ -88,9 +98,7 @@ int m5602_read_sensor(struct sd *sd, const u8 address,
if (!len || len > sd->sensor->i2c_regW) if (!len || len > sd->sensor->i2c_regW)
return -EINVAL; return -EINVAL;
do { err = m5602_wait_for_i2c(sd);
err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
} while ((*i2c_data & I2C_BUSY) && !err);
if (err < 0) if (err < 0)
return err; return err;
...@@ -118,6 +126,10 @@ int m5602_read_sensor(struct sd *sd, const u8 address, ...@@ -118,6 +126,10 @@ int m5602_read_sensor(struct sd *sd, const u8 address,
} }
for (i = 0; (i < len) && !err; i++) { for (i = 0; (i < len) && !err; i++) {
err = m5602_wait_for_i2c(sd);
if (err < 0)
return err;
err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
PDEBUG(D_CONF, "Reading sensor register " PDEBUG(D_CONF, "Reading sensor register "
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
* *
*/ */
#include <linux/kthread.h>
#include "m5602_s5k83a.h" #include "m5602_s5k83a.h"
static struct v4l2_pix_format s5k83a_modes[] = { static struct v4l2_pix_format s5k83a_modes[] = {
...@@ -33,47 +34,54 @@ static struct v4l2_pix_format s5k83a_modes[] = { ...@@ -33,47 +34,54 @@ static struct v4l2_pix_format s5k83a_modes[] = {
}; };
const static struct ctrl s5k83a_ctrls[] = { const static struct ctrl s5k83a_ctrls[] = {
#define GAIN_IDX 0
{ {
{ {
.id = V4L2_CID_BRIGHTNESS, .id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "brightness", .name = "gain",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0xff, .maximum = 0xff,
.step = 0x01, .step = 0x01,
.default_value = S5K83A_DEFAULT_BRIGHTNESS, .default_value = S5K83A_DEFAULT_GAIN,
.flags = V4L2_CTRL_FLAG_SLIDER .flags = V4L2_CTRL_FLAG_SLIDER
}, },
.set = s5k83a_set_brightness, .set = s5k83a_set_gain,
.get = s5k83a_get_brightness .get = s5k83a_get_gain
}, { },
#define BRIGHTNESS_IDX 1
{
{ {
.id = V4L2_CID_WHITENESS, .id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "whiteness", .name = "brightness",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0xff, .maximum = 0xff,
.step = 0x01, .step = 0x01,
.default_value = S5K83A_DEFAULT_WHITENESS, .default_value = S5K83A_DEFAULT_BRIGHTNESS,
.flags = V4L2_CTRL_FLAG_SLIDER .flags = V4L2_CTRL_FLAG_SLIDER
}, },
.set = s5k83a_set_whiteness, .set = s5k83a_set_brightness,
.get = s5k83a_get_whiteness, .get = s5k83a_get_brightness,
}, { },
#define EXPOSURE_IDX 2
{ {
.id = V4L2_CID_GAIN, {
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain", .name = "exposure",
.minimum = 0x00, .minimum = 0x00,
.maximum = S5K83A_MAXIMUM_GAIN, .maximum = S5K83A_MAXIMUM_EXPOSURE,
.step = 0x01, .step = 0x01,
.default_value = S5K83A_DEFAULT_GAIN, .default_value = S5K83A_DEFAULT_EXPOSURE,
.flags = V4L2_CTRL_FLAG_SLIDER .flags = V4L2_CTRL_FLAG_SLIDER
}, },
.set = s5k83a_set_gain, .set = s5k83a_set_exposure,
.get = s5k83a_get_gain .get = s5k83a_get_exposure
}, { },
#define HFLIP_IDX 3
{
{ {
.id = V4L2_CID_HFLIP, .id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN, .type = V4L2_CTRL_TYPE_BOOLEAN,
...@@ -85,7 +93,9 @@ const static struct ctrl s5k83a_ctrls[] = { ...@@ -85,7 +93,9 @@ const static struct ctrl s5k83a_ctrls[] = {
}, },
.set = s5k83a_set_hflip, .set = s5k83a_set_hflip,
.get = s5k83a_get_hflip .get = s5k83a_get_hflip
}, { },
#define VFLIP_IDX 4
{
{ {
.id = V4L2_CID_VFLIP, .id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN, .type = V4L2_CTRL_TYPE_BOOLEAN,
...@@ -101,9 +111,13 @@ const static struct ctrl s5k83a_ctrls[] = { ...@@ -101,9 +111,13 @@ const static struct ctrl s5k83a_ctrls[] = {
}; };
static void s5k83a_dump_registers(struct sd *sd); static void s5k83a_dump_registers(struct sd *sd);
static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
static int s5k83a_set_led_indication(struct sd *sd, u8 val);
int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, __s32 vflip, __s32 hflip);
int s5k83a_probe(struct sd *sd) int s5k83a_probe(struct sd *sd)
{ {
struct s5k83a_priv *sens_priv;
u8 prod_id = 0, ver_id = 0; u8 prod_id = 0, ver_id = 0;
int i, err = 0; int i, err = 0;
...@@ -145,10 +159,28 @@ int s5k83a_probe(struct sd *sd) ...@@ -145,10 +159,28 @@ int s5k83a_probe(struct sd *sd)
info("Detected a s5k83a sensor"); info("Detected a s5k83a sensor");
sensor_found: sensor_found:
sens_priv = kmalloc(
sizeof(struct s5k83a_priv), GFP_KERNEL);
if (!sens_priv)
return -ENOMEM;
sens_priv->settings =
kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL);
if (!sens_priv->settings)
return -ENOMEM;
sd->gspca_dev.cam.cam_mode = s5k83a_modes; sd->gspca_dev.cam.cam_mode = s5k83a_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
sd->desc->ctrls = s5k83a_ctrls; sd->desc->ctrls = s5k83a_ctrls;
sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls); sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls);
/* null the pointer! thread is't running now */
sens_priv->rotation_thread = NULL;
for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++)
sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value;
sd->sensor_priv = sens_priv;
return 0; return 0;
} }
...@@ -190,84 +222,104 @@ int s5k83a_init(struct sd *sd) ...@@ -190,84 +222,104 @@ int s5k83a_init(struct sd *sd)
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
} }
static int rotation_thread_function(void *data)
{
struct sd *sd = (struct sd *) data;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
u8 reg, previous_rotation = 0;
__s32 vflip, hflip;
set_current_state(TASK_INTERRUPTIBLE);
while (!schedule_timeout(100)) {
if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
break;
s5k83a_get_rotation(sd, &reg);
if (previous_rotation != reg) {
previous_rotation = reg;
info("Camera was flipped");
s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
if (reg) {
vflip = !vflip;
hflip = !hflip;
}
s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
}
mutex_unlock(&sd->gspca_dev.usb_lock);
set_current_state(TASK_INTERRUPTIBLE);
}
/* return to "front" flip */
if (previous_rotation) {
s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
}
sens_priv->rotation_thread = NULL;
return 0;
}
int s5k83a_start(struct sd *sd) int s5k83a_start(struct sd *sd)
{ {
struct s5k83a_priv *sens_priv = sd->sensor_priv;
/* Create another thread, polling the GPIO ports of the camera to check
if it got rotated. This is how the windows driver does it so we have
to assume that there is no better way of accomplishing this */
sens_priv->rotation_thread = kthread_create(rotation_thread_function, sd, "rotation thread");
wake_up_process(sens_priv->rotation_thread);
return s5k83a_set_led_indication(sd, 1); return s5k83a_set_led_indication(sd, 1);
} }
int s5k83a_stop(struct sd *sd) int s5k83a_stop(struct sd *sd)
{ {
return s5k83a_set_led_indication(sd, 0); struct s5k83a_priv *sens_priv = sd->sensor_priv;
}
int s5k83a_power_down(struct sd *sd) if (sens_priv->rotation_thread)
{ kthread_stop(sens_priv->rotation_thread);
return 0;
return s5k83a_set_led_indication(sd, 0);
} }
static void s5k83a_dump_registers(struct sd *sd) void s5k83a_disconnect(struct sd *sd)
{ {
int address; struct s5k83a_priv *sens_priv = sd->sensor_priv;
u8 page, old_page;
m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Dumping the s5k83a register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
m5602_read_sensor(sd, address, &val, 1);
info("register 0x%x contains 0x%x",
address, val);
}
}
info("s5k83a register state dump complete");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Probing for which registers that are read/write "
"for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
m5602_read_sensor(sd, address, &old_val, 1); s5k83a_stop(sd);
m5602_write_sensor(sd, address, &test_val, 1);
m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val) sd->sensor = NULL;
info("register 0x%x is writeable", address); kfree(sens_priv->settings);
else kfree(sens_priv);
info("register 0x%x is read only", address); }
/* Restore original val */ int s5k83a_power_down(struct sd *sd)
m5602_write_sensor(sd, address, &old_val, 1); {
} return 0;
}
info("Read/write register probing complete");
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
} }
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{ {
int err;
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2); *val = sens_priv->settings[GAIN_IDX];
if (err < 0) return 0;
return err;
data[1] = data[1] << 1;
*val = data[1];
return err;
} }
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{ {
int err; int err;
u8 data[2]; u8 data[2];
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[GAIN_IDX] = val;
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x20; data[1] = 0x20;
...@@ -283,89 +335,68 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) ...@@ -283,89 +335,68 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
/* FIXME: This is not sane, we need to figure out the composition /* FIXME: This is not sane, we need to figure out the composition
of these registers */ of these registers */
data[0] = val >> 3; /* brightness, high 5 bits */ data[0] = val >> 3; /* gain, high 5 bits */
data[1] = val >> 1; /* brightness, high 7 bits */ data[1] = val >> 1; /* gain, high 7 bits */
err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2); err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
return err; return err;
} }
int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val) int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
{ {
int err;
u8 data;
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1); *val = sens_priv->settings[BRIGHTNESS_IDX];
if (err < 0) return 0;
return err;
*val = data;
return err;
} }
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val) int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
{ {
int err; int err;
u8 data[1]; u8 data[1];
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[BRIGHTNESS_IDX] = val;
data[0] = val; data[0] = val;
err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1); err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
return err; return err;
} }
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{ {
int err;
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2); *val = sens_priv->settings[EXPOSURE_IDX];
if (err < 0) return 0;
return err;
data[1] = data[1] & 0x3f;
if (data[1] > S5K83A_MAXIMUM_GAIN)
data[1] = S5K83A_MAXIMUM_GAIN;
*val = data[1];
return err;
} }
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{ {
int err; int err;
u8 data[2]; u8 data[2];
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
sens_priv->settings[EXPOSURE_IDX] = val;
data[0] = 0; data[0] = 0;
data[1] = val; data[1] = val;
err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
return err; return err;
} }
int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{ {
int err;
u8 data[1];
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
data[0] = 0x05; *val = sens_priv->settings[VFLIP_IDX];
err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); return 0;
if (err < 0)
return err;
err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x40) ? 1 : 0;
return err;
} }
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, __s32 vflip, __s32 hflip)
{ {
int err; int err;
u8 data[1]; u8 data[1];
...@@ -376,69 +407,83 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) ...@@ -376,69 +407,83 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
if (err < 0) if (err < 0)
return err; return err;
err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); /* six bit is vflip, seven is hflip */
if (err < 0) data[0] = S5K83A_FLIP_MASK;
return err; data[0] = (vflip) ? data[0] | 0x40 : data[0];
data[0] = (hflip) ? data[0] | 0x80 : data[0];
/* set or zero six bit, seven is hflip */
data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK
: (data[0] & 0x80) | S5K83A_FLIP_MASK;
err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0) if (err < 0)
return err; return err;
data[0] = (val) ? 0x0b : 0x0a; data[0] = (vflip) ? 0x0b : 0x0a;
err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
if (err < 0)
return err;
data[0] = (hflip) ? 0x0a : 0x0b;
err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
return err; return err;
} }
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{ {
int err; int err;
u8 data[1]; u8 reg;
__s32 hflip;
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
data[0] = 0x05; sens_priv->settings[VFLIP_IDX] = val;
err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
s5k83a_get_hflip(gspca_dev, &hflip);
err = s5k83a_get_rotation(sd, &reg);
if (err < 0) if (err < 0)
return err; return err;
if (reg) {
val = !val;
hflip = !hflip;
}
err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); err = s5k83a_set_flip_real(gspca_dev, val, hflip);
*val = (data[0] | 0x80) ? 1 : 0;
return err; return err;
} }
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
*val = sens_priv->settings[HFLIP_IDX];
return 0;
}
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{ {
int err; int err;
u8 data[1]; u8 reg;
__s32 vflip;
struct sd *sd = (struct sd *) gspca_dev; struct sd *sd = (struct sd *) gspca_dev;
struct s5k83a_priv *sens_priv = sd->sensor_priv;
data[0] = 0x05; sens_priv->settings[HFLIP_IDX] = val;
err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
return err;
err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); s5k83a_get_vflip(gspca_dev, &vflip);
if (err < 0)
return err;
/* set or zero seven bit, six is vflip */ err = s5k83a_get_rotation(sd, &reg);
data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK
: (data[0] & 0x40) | S5K83A_FLIP_MASK;
err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0) if (err < 0)
return err; return err;
if (reg) {
val = !val;
vflip = !vflip;
}
data[0] = (val) ? 0x0a : 0x0b; err = s5k83a_set_flip_real(gspca_dev, vflip, val);
err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
return err; return err;
} }
int s5k83a_set_led_indication(struct sd *sd, u8 val) static int s5k83a_set_led_indication(struct sd *sd, u8 val)
{ {
int err = 0; int err = 0;
u8 data[1]; u8 data[1];
...@@ -456,3 +501,53 @@ int s5k83a_set_led_indication(struct sd *sd, u8 val) ...@@ -456,3 +501,53 @@ int s5k83a_set_led_indication(struct sd *sd, u8 val)
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
} }
/* Get camera rotation on Acer notebooks */
static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
{
int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
*reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
return err;
}
static void s5k83a_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Dumping the s5k83a register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
m5602_read_sensor(sd, address, &val, 1);
info("register 0x%x contains 0x%x",
address, val);
}
}
info("s5k83a register state dump complete");
for (page = 0; page < 16; page++) {
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Probing for which registers that are read/write "
"for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
m5602_read_sensor(sd, address, &old_val, 1);
m5602_write_sensor(sd, address, &test_val, 1);
m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val)
info("register 0x%x is writeable", address);
else
info("register 0x%x is read only", address);
/* Restore original val */
m5602_write_sensor(sd, address, &old_val, 1);
}
}
info("Read/write register probing complete");
m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
...@@ -24,17 +24,18 @@ ...@@ -24,17 +24,18 @@
#define S5K83A_FLIP 0x01 #define S5K83A_FLIP 0x01
#define S5K83A_HFLIP_TUNE 0x03 #define S5K83A_HFLIP_TUNE 0x03
#define S5K83A_VFLIP_TUNE 0x05 #define S5K83A_VFLIP_TUNE 0x05
#define S5K83A_WHITENESS 0x0a #define S5K83A_BRIGHTNESS 0x0a
#define S5K83A_GAIN 0x18 #define S5K83A_EXPOSURE 0x18
#define S5K83A_BRIGHTNESS 0x1b #define S5K83A_GAIN 0x1b
#define S5K83A_PAGE_MAP 0xec #define S5K83A_PAGE_MAP 0xec
#define S5K83A_DEFAULT_BRIGHTNESS 0x71 #define S5K83A_DEFAULT_GAIN 0x71
#define S5K83A_DEFAULT_WHITENESS 0x7e #define S5K83A_DEFAULT_BRIGHTNESS 0x7e
#define S5K83A_DEFAULT_GAIN 0x00 #define S5K83A_DEFAULT_EXPOSURE 0x00
#define S5K83A_MAXIMUM_GAIN 0x3c #define S5K83A_MAXIMUM_EXPOSURE 0x3c
#define S5K83A_FLIP_MASK 0x10 #define S5K83A_FLIP_MASK 0x10
#define S5K83A_GPIO_LED_MASK 0x10 #define S5K83A_GPIO_LED_MASK 0x10
#define S5K83A_GPIO_ROTATION_MASK 0x40
/*****************************************************************************/ /*****************************************************************************/
...@@ -47,15 +48,14 @@ int s5k83a_init(struct sd *sd); ...@@ -47,15 +48,14 @@ int s5k83a_init(struct sd *sd);
int s5k83a_start(struct sd *sd); int s5k83a_start(struct sd *sd);
int s5k83a_stop(struct sd *sd); int s5k83a_stop(struct sd *sd);
int s5k83a_power_down(struct sd *sd); int s5k83a_power_down(struct sd *sd);
void s5k83a_disconnect(struct sd *sd);
int s5k83a_set_led_indication(struct sd *sd, u8 val);
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
...@@ -68,10 +68,18 @@ static const struct m5602_sensor s5k83a = { ...@@ -68,10 +68,18 @@ static const struct m5602_sensor s5k83a = {
.start = s5k83a_start, .start = s5k83a_start,
.stop = s5k83a_stop, .stop = s5k83a_stop,
.power_down = s5k83a_power_down, .power_down = s5k83a_power_down,
.disconnect = s5k83a_disconnect,
.i2c_slave_id = 0x5a, .i2c_slave_id = 0x5a,
.i2c_regW = 2, .i2c_regW = 2,
}; };
struct s5k83a_priv {
/* We use another thread periodically
probing the orientation of the camera */
struct task_struct *rotation_thread;
s32 *settings;
};
static const unsigned char preinit_s5k83a[][4] = static const unsigned char preinit_s5k83a[][4] =
{ {
{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
...@@ -125,7 +133,7 @@ static const unsigned char init_s5k83a[][4] = ...@@ -125,7 +133,7 @@ static const unsigned char init_s5k83a[][4] =
{SENSOR, 0x01, 0x50, 0x00}, {SENSOR, 0x01, 0x50, 0x00},
{SENSOR, 0x12, 0x20, 0x00}, {SENSOR, 0x12, 0x20, 0x00},
{SENSOR, 0x17, 0x40, 0x00}, {SENSOR, 0x17, 0x40, 0x00},
{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, {SENSOR, S5K83A_GAIN, 0x0f, 0x00},
{SENSOR, 0x1c, 0x00, 0x00}, {SENSOR, 0x1c, 0x00, 0x00},
{SENSOR, 0x02, 0x70, 0x00}, {SENSOR, 0x02, 0x70, 0x00},
{SENSOR, 0x03, 0x0b, 0x00}, {SENSOR, 0x03, 0x0b, 0x00},
...@@ -232,7 +240,7 @@ static const unsigned char init_s5k83a[][4] = ...@@ -232,7 +240,7 @@ static const unsigned char init_s5k83a[][4] =
{SENSOR, 0x01, 0x50, 0x00}, {SENSOR, 0x01, 0x50, 0x00},
{SENSOR, 0x12, 0x20, 0x00}, {SENSOR, 0x12, 0x20, 0x00},
{SENSOR, 0x17, 0x40, 0x00}, {SENSOR, 0x17, 0x40, 0x00},
{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, {SENSOR, S5K83A_GAIN, 0x0f, 0x00},
{SENSOR, 0x1c, 0x00, 0x00}, {SENSOR, 0x1c, 0x00, 0x00},
{SENSOR, 0x02, 0x70, 0x00}, {SENSOR, 0x02, 0x70, 0x00},
/* some values like 0x10 give a blue-purple image */ /* some values like 0x10 give a blue-purple image */
...@@ -320,7 +328,7 @@ static const unsigned char init_s5k83a[][4] = ...@@ -320,7 +328,7 @@ static const unsigned char init_s5k83a[][4] =
{SENSOR, 0x01, 0x50, 0x00}, {SENSOR, 0x01, 0x50, 0x00},
{SENSOR, 0x12, 0x20, 0x00}, {SENSOR, 0x12, 0x20, 0x00},
{SENSOR, 0x17, 0x40, 0x00}, {SENSOR, 0x17, 0x40, 0x00},
{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, {SENSOR, S5K83A_GAIN, 0x0f, 0x00},
{SENSOR, 0x1c, 0x00, 0x00}, {SENSOR, 0x1c, 0x00, 0x00},
{SENSOR, 0x02, 0x70, 0x00}, {SENSOR, 0x02, 0x70, 0x00},
{SENSOR, 0x03, 0x0b, 0x00}, {SENSOR, 0x03, 0x0b, 0x00},
...@@ -374,24 +382,23 @@ static const unsigned char init_s5k83a[][4] = ...@@ -374,24 +382,23 @@ static const unsigned char init_s5k83a[][4] =
(this is value after boot, but after tries can be different) */ (this is value after boot, but after tries can be different) */
{SENSOR, 0x00, 0x06, 0x00}, {SENSOR, 0x00, 0x06, 0x00},
/* set default brightness */ /* set default gain */
{SENSOR_LONG, 0x14, 0x00, 0x20}, {SENSOR_LONG, 0x14, 0x00, 0x20},
{SENSOR_LONG, 0x0d, 0x01, 0x00}, {SENSOR_LONG, 0x0d, 0x01, 0x00},
{SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3, {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_GAIN >> 3,
S5K83A_DEFAULT_BRIGHTNESS >> 1}, S5K83A_DEFAULT_GAIN >> 1},
/* set default whiteness */ /* set default brightness */
{SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00}, {SENSOR, S5K83A_BRIGHTNESS, S5K83A_DEFAULT_BRIGHTNESS, 0x00},
/* set default gain */ /* set default exposure */
{SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN}, {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_EXPOSURE},
/* set default flip */ /* set default flip */
{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
{SENSOR, S5K83A_FLIP, 0x00 | S5K83A_FLIP_MASK, 0x00}, {SENSOR, S5K83A_FLIP, 0x00 | S5K83A_FLIP_MASK, 0x00},
{SENSOR, S5K83A_HFLIP_TUNE, 0x0b, 0x00}, {SENSOR, S5K83A_HFLIP_TUNE, 0x0b, 0x00},
{SENSOR, S5K83A_VFLIP_TUNE, 0x0a, 0x00} {SENSOR, S5K83A_VFLIP_TUNE, 0x0a, 0x00}
}; };
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment