V4L/DVB (11451): gspca - m5602-s5k83a: Add rotation, ctrl cache. Rename some ctrls.
authorLuk?? Karas <lukas.karas@centrum.cz>
Fri, 3 Apr 2009 05:45:56 +0000 (02:45 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 16 Jun 2009 21:20:23 +0000 (18:20 -0300)
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: Luk?? Karas <lukas.karas@centrum.cz>
Signed-off-by: Erik Andr?n <erik.andren@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/m5602/m5602_core.c
drivers/media/video/gspca/m5602/m5602_s5k83a.c
drivers/media/video/gspca/m5602/m5602_s5k83a.h

index 1aac298..93302f3 100644 (file)
@@ -80,6 +80,16 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
        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,
                       u8 *i2c_data, const u8 len)
 {
@@ -88,9 +98,7 @@ int m5602_read_sensor(struct sd *sd, const u8 address,
        if (!len || len > sd->sensor->i2c_regW)
                return -EINVAL;
 
-       do {
-               err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
-       } while ((*i2c_data & I2C_BUSY) && !err);
+       err = m5602_wait_for_i2c(sd);
        if (err < 0)
                return err;
 
@@ -118,6 +126,10 @@ int m5602_read_sensor(struct sd *sd, const u8 address,
        }
 
        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]));
 
                PDEBUG(D_CONF, "Reading sensor register "
index 42c86aa..c77afca 100644 (file)
@@ -16,6 +16,7 @@
  *
  */
 
+#include <linux/kthread.h>
 #include "m5602_s5k83a.h"
 
 static struct v4l2_pix_format s5k83a_modes[] = {
@@ -33,47 +34,54 @@ static struct v4l2_pix_format s5k83a_modes[] = {
 };
 
 const static struct ctrl s5k83a_ctrls[] = {
+#define GAIN_IDX 0
        {
                {
-                       .id = V4L2_CID_BRIGHTNESS,
+                       .id = V4L2_CID_GAIN,
                        .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "brightness",
+                       .name = "gain",
                        .minimum = 0x00,
                        .maximum = 0xff,
                        .step = 0x01,
-                       .default_value = S5K83A_DEFAULT_BRIGHTNESS,
+                       .default_value = S5K83A_DEFAULT_GAIN,
                        .flags = V4L2_CTRL_FLAG_SLIDER
                },
-                       .set = s5k83a_set_brightness,
-                       .get = s5k83a_get_brightness
+                       .set = s5k83a_set_gain,
+                       .get = s5k83a_get_gain
 
-       }, {
+       },
+#define BRIGHTNESS_IDX 1
+       {
                {
-                       .id = V4L2_CID_WHITENESS,
+                       .id = V4L2_CID_BRIGHTNESS,
                        .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "whiteness",
+                       .name = "brightness",
                        .minimum = 0x00,
                        .maximum = 0xff,
                        .step = 0x01,
-                       .default_value = S5K83A_DEFAULT_WHITENESS,
+                       .default_value = S5K83A_DEFAULT_BRIGHTNESS,
                        .flags = V4L2_CTRL_FLAG_SLIDER
                },
-                       .set = s5k83a_set_whiteness,
-                       .get = s5k83a_get_whiteness,
-       }, {
+                       .set = s5k83a_set_brightness,
+                       .get = s5k83a_get_brightness,
+       },
+#define EXPOSURE_IDX 2
+       {
                {
-                       .id = V4L2_CID_GAIN,
+                       .id = V4L2_CID_EXPOSURE,
                        .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "gain",
+                       .name = "exposure",
                        .minimum = 0x00,
-                       .maximum = S5K83A_MAXIMUM_GAIN,
+                       .maximum = S5K83A_MAXIMUM_EXPOSURE,
                        .step = 0x01,
-                       .default_value = S5K83A_DEFAULT_GAIN,
+                       .default_value = S5K83A_DEFAULT_EXPOSURE,
                        .flags = V4L2_CTRL_FLAG_SLIDER
                },
-                       .set = s5k83a_set_gain,
-                       .get = s5k83a_get_gain
-       }, {
+                       .set = s5k83a_set_exposure,
+                       .get = s5k83a_get_exposure
+       },
+#define HFLIP_IDX 3
+       {
                {
                        .id         = V4L2_CID_HFLIP,
                        .type       = V4L2_CTRL_TYPE_BOOLEAN,
@@ -85,7 +93,9 @@ const static struct ctrl s5k83a_ctrls[] = {
                },
                        .set = s5k83a_set_hflip,
                        .get = s5k83a_get_hflip
-       }, {
+       },
+#define VFLIP_IDX 4
+       {
                {
                 .id         = V4L2_CID_VFLIP,
                .type       = V4L2_CTRL_TYPE_BOOLEAN,
@@ -101,9 +111,13 @@ const static struct ctrl s5k83a_ctrls[] = {
 };
 
 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)
 {
+       struct s5k83a_priv *sens_priv;
        u8 prod_id = 0, ver_id = 0;
        int i, err = 0;
 
@@ -145,10 +159,28 @@ int s5k83a_probe(struct sd *sd)
                info("Detected a s5k83a sensor");
 
 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.nmodes = ARRAY_SIZE(s5k83a_modes);
        sd->desc->ctrls = 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;
 }
 
@@ -190,84 +222,104 @@ int s5k83a_init(struct sd *sd)
        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)
 {
+       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);
 }
 
 int s5k83a_stop(struct sd *sd)
 {
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+       if (sens_priv->rotation_thread)
+               kthread_stop(sens_priv->rotation_thread);
+
        return s5k83a_set_led_indication(sd, 0);
 }
 
-int s5k83a_power_down(struct sd *sd)
+void s5k83a_disconnect(struct sd *sd)
 {
-       return 0;
-}
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-static void s5k83a_dump_registers(struct sd *sd)
-{
-       int address;
-       u8 page, old_page;
-       m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+       s5k83a_stop(sd);
 
-       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);
+       sd->sensor = NULL;
+       kfree(sens_priv->settings);
+       kfree(sens_priv);
+}
 
-                       /* 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);
+int s5k83a_power_down(struct sd *sd)
+{
+       return 0;
 }
 
-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 s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
-       if (err < 0)
-               return err;
-
-       data[1] = data[1] << 1;
-       *val = data[1];
-
-       return err;
+       *val = sens_priv->settings[GAIN_IDX];
+       return 0;
 }
 
-int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
 {
        int err;
        u8 data[2];
        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[1] = 0x20;
@@ -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
                  of these registers */
-       data[0] = val >> 3; /* brightness, high 5 bits */
-       data[1] = val >> 1; /* brightness, high 7 bits */
-       err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+       data[0] = val >> 3; /* gain, high 5 bits */
+       data[1] = val >> 1; /* gain, high 7 bits */
+       err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
 
        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 s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
-       if (err < 0)
-               return err;
-
-       *val = data;
-
-       return err;
+       *val = sens_priv->settings[BRIGHTNESS_IDX];
+       return 0;
 }
 
-int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
+int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
 {
        int err;
        u8 data[1];
        struct sd *sd = (struct sd *) gspca_dev;
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
+       sens_priv->settings[BRIGHTNESS_IDX] = val;
        data[0] = val;
-       err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1);
-
+       err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
        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 s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2);
-       if (err < 0)
-               return err;
-
-       data[1] = data[1] & 0x3f;
-       if (data[1] > S5K83A_MAXIMUM_GAIN)
-               data[1] = S5K83A_MAXIMUM_GAIN;
-
-       *val = data[1];
-
-       return err;
+       *val = sens_priv->settings[EXPOSURE_IDX];
+       return 0;
 }
 
-int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
 {
        int err;
        u8 data[2];
        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[1] = val;
-       err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
+       err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
        return err;
 }
 
 int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
 {
-       int err;
-       u8 data[1];
        struct sd *sd = (struct sd *) gspca_dev;
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       data[0] = 0x05;
-       err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
-       if (err < 0)
-               return err;
-
-       err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
-       *val = (data[0] | 0x40) ? 1 : 0;
-
-       return err;
+       *val = sens_priv->settings[VFLIP_IDX];
+       return 0;
 }
 
-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;
        u8 data[1];
@@ -376,69 +407,83 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
        if (err < 0)
                return err;
 
-       err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
-       if (err < 0)
-               return err;
+       /* six bit is vflip, seven is hflip */
+       data[0] = S5K83A_FLIP_MASK;
+       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);
        if (err < 0)
                return err;
 
-       data[0] = (val) ? 0x0b : 0x0a;
+       data[0] = (vflip) ? 0x0b : 0x0a;
        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;
 }
 
-int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
 {
        int err;
-       u8 data[1];
+       u8 reg;
+       __s32 hflip;
        struct sd *sd = (struct sd *) gspca_dev;
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       data[0] = 0x05;
-       err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+       sens_priv->settings[VFLIP_IDX] = val;
+
+       s5k83a_get_hflip(gspca_dev, &hflip);
+
+       err = s5k83a_get_rotation(sd, &reg);
        if (err < 0)
                return err;
+       if (reg) {
+               val = !val;
+               hflip = !hflip;
+       }
 
-       err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
-       *val = (data[0] | 0x80) ? 1 : 0;
-
+       err = s5k83a_set_flip_real(gspca_dev, val, hflip);
        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 err;
-       u8 data[1];
+       u8 reg;
+       __s32 vflip;
        struct sd *sd = (struct sd *) gspca_dev;
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       data[0] = 0x05;
-       err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
-       if (err < 0)
-               return err;
+       sens_priv->settings[HFLIP_IDX] = val;
 
-       err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
-       if (err < 0)
-               return err;
+       s5k83a_get_vflip(gspca_dev, &vflip);
 
-       /* set or zero seven bit, six is vflip */
-       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);
+       err = s5k83a_get_rotation(sd, &reg);
        if (err < 0)
                return err;
+       if (reg) {
+               val = !val;
+               vflip = !vflip;
+       }
 
-       data[0] = (val) ? 0x0a : 0x0b;
-       err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
-
+       err = s5k83a_set_flip_real(gspca_dev, vflip, val);
        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;
        u8 data[1];
@@ -456,3 +501,53 @@ int s5k83a_set_led_indication(struct sd *sd, u8 val)
 
        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);
+}
index 819ab25..9ca3ca3 100644 (file)
 
 #include "m5602_sensor.h"
 
-#define S5K83A_FLIP                            0x01
-#define S5K83A_HFLIP_TUNE                      0x03
-#define S5K83A_VFLIP_TUNE                      0x05
-#define S5K83A_WHITENESS                       0x0a
-#define S5K83A_GAIN                            0x18
-#define S5K83A_BRIGHTNESS                      0x1b
-#define S5K83A_PAGE_MAP                                0xec
-
-#define S5K83A_DEFAULT_BRIGHTNESS              0x71
-#define S5K83A_DEFAULT_WHITENESS               0x7e
-#define S5K83A_DEFAULT_GAIN                    0x00
-#define S5K83A_MAXIMUM_GAIN                    0x3c
-#define S5K83A_FLIP_MASK                       0x10
+#define S5K83A_FLIP                    0x01
+#define S5K83A_HFLIP_TUNE              0x03
+#define S5K83A_VFLIP_TUNE              0x05
+#define S5K83A_BRIGHTNESS              0x0a
+#define S5K83A_EXPOSURE                        0x18
+#define S5K83A_GAIN                    0x1b
+#define S5K83A_PAGE_MAP                        0xec
+
+#define S5K83A_DEFAULT_GAIN            0x71
+#define S5K83A_DEFAULT_BRIGHTNESS      0x7e
+#define S5K83A_DEFAULT_EXPOSURE                0x00
+#define S5K83A_MAXIMUM_EXPOSURE                0x3c
+#define S5K83A_FLIP_MASK               0x10
 #define S5K83A_GPIO_LED_MASK           0x10
+#define S5K83A_GPIO_ROTATION_MASK      0x40
 
 /*****************************************************************************/
 
@@ -47,15 +48,14 @@ int s5k83a_init(struct sd *sd);
 int s5k83a_start(struct sd *sd);
 int s5k83a_stop(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_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_set_vflip(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 = {
        .start = s5k83a_start,
        .stop = s5k83a_stop,
        .power_down = s5k83a_power_down,
+       .disconnect = s5k83a_disconnect,
        .i2c_slave_id = 0x5a,
        .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] =
 {
        {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
@@ -125,7 +133,7 @@ static const unsigned char init_s5k83a[][4] =
        {SENSOR, 0x01, 0x50, 0x00},
        {SENSOR, 0x12, 0x20, 0x00},
        {SENSOR, 0x17, 0x40, 0x00},
-       {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+       {SENSOR, S5K83A_GAIN, 0x0f, 0x00},
        {SENSOR, 0x1c, 0x00, 0x00},
        {SENSOR, 0x02, 0x70, 0x00},
        {SENSOR, 0x03, 0x0b, 0x00},
@@ -232,7 +240,7 @@ static const unsigned char init_s5k83a[][4] =
        {SENSOR, 0x01, 0x50, 0x00},
        {SENSOR, 0x12, 0x20, 0x00},
        {SENSOR, 0x17, 0x40, 0x00},
-       {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+       {SENSOR, S5K83A_GAIN, 0x0f, 0x00},
        {SENSOR, 0x1c, 0x00, 0x00},
        {SENSOR, 0x02, 0x70, 0x00},
        /* some values like 0x10 give a blue-purple image */
@@ -320,7 +328,7 @@ static const unsigned char init_s5k83a[][4] =
        {SENSOR, 0x01, 0x50, 0x00},
        {SENSOR, 0x12, 0x20, 0x00},
        {SENSOR, 0x17, 0x40, 0x00},
-       {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+       {SENSOR, S5K83A_GAIN, 0x0f, 0x00},
        {SENSOR, 0x1c, 0x00, 0x00},
        {SENSOR, 0x02, 0x70, 0x00},
        {SENSOR, 0x03, 0x0b, 0x00},
@@ -374,24 +382,23 @@ static const unsigned char init_s5k83a[][4] =
           (this is value after boot, but after tries can be different) */
        {SENSOR, 0x00, 0x06, 0x00},
 
-       /* set default brightness */
+       /* set default gain */
        {SENSOR_LONG, 0x14, 0x00, 0x20},
        {SENSOR_LONG, 0x0d, 0x01, 0x00},
-       {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3,
-                           S5K83A_DEFAULT_BRIGHTNESS >> 1},
+       {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_GAIN >> 3,
+               S5K83A_DEFAULT_GAIN >> 1},
 
-       /* set default whiteness */
-       {SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00},
+       /* set default brightness */
+       {SENSOR, S5K83A_BRIGHTNESS, S5K83A_DEFAULT_BRIGHTNESS, 0x00},
 
-       /* set default gain */
-       {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN},
+       /* set default exposure */
+       {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_EXPOSURE},
 
        /* set default flip */
        {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
        {SENSOR, S5K83A_FLIP, 0x00 | S5K83A_FLIP_MASK, 0x00},
        {SENSOR, S5K83A_HFLIP_TUNE, 0x0b, 0x00},
        {SENSOR, S5K83A_VFLIP_TUNE, 0x0a, 0x00}
-
 };
 
 #endif