include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / media / video / gspca / m5602 / m5602_s5k83a.c
index 84a648d..6b3be4f 100644 (file)
  *
  */
 
+#include <linux/kthread.h>
+#include <linux/slab.h>
 #include "m5602_s5k83a.h"
 
+static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct v4l2_pix_format s5k83a_modes[] = {
+       {
+               640,
+               480,
+               V4L2_PIX_FMT_SBGGR8,
+               V4L2_FIELD_NONE,
+               .sizeimage =
+                       640 * 480,
+               .bytesperline = 640,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0
+       }
+};
+
+static const struct ctrl s5k83a_ctrls[] = {
+#define GAIN_IDX 0
+       {
+               {
+                       .id = V4L2_CID_GAIN,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "gain",
+                       .minimum = 0x00,
+                       .maximum = 0xff,
+                       .step = 0x01,
+                       .default_value = S5K83A_DEFAULT_GAIN,
+                       .flags = V4L2_CTRL_FLAG_SLIDER
+               },
+                       .set = s5k83a_set_gain,
+                       .get = s5k83a_get_gain
+
+       },
+#define BRIGHTNESS_IDX 1
+       {
+               {
+                       .id = V4L2_CID_BRIGHTNESS,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "brightness",
+                       .minimum = 0x00,
+                       .maximum = 0xff,
+                       .step = 0x01,
+                       .default_value = S5K83A_DEFAULT_BRIGHTNESS,
+                       .flags = V4L2_CTRL_FLAG_SLIDER
+               },
+                       .set = s5k83a_set_brightness,
+                       .get = s5k83a_get_brightness,
+       },
+#define EXPOSURE_IDX 2
+       {
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "exposure",
+                       .minimum = 0x00,
+                       .maximum = S5K83A_MAXIMUM_EXPOSURE,
+                       .step = 0x01,
+                       .default_value = S5K83A_DEFAULT_EXPOSURE,
+                       .flags = V4L2_CTRL_FLAG_SLIDER
+               },
+                       .set = s5k83a_set_exposure,
+                       .get = s5k83a_get_exposure
+       },
+#define HFLIP_IDX 3
+       {
+               {
+                       .id = V4L2_CID_HFLIP,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "horizontal flip",
+                       .minimum = 0,
+                       .maximum = 1,
+                       .step = 1,
+                       .default_value = 0
+               },
+                       .set = s5k83a_set_hflip,
+                       .get = s5k83a_get_hflip
+       },
+#define VFLIP_IDX 4
+       {
+               {
+                       .id = V4L2_CID_VFLIP,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "vertical flip",
+                       .minimum = 0,
+                       .maximum = 1,
+                       .step = 1,
+                       .default_value = 0
+               },
+               .set = s5k83a_set_vflip,
+               .get = s5k83a_get_vflip
+       }
+};
+
 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);
+static 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;
 
@@ -35,7 +144,7 @@ int s5k83a_probe(struct sd *sd)
                return -ENODEV;
        }
 
-       info("Probing for a s5k83a sensor");
+       PDEBUG(D_PROBE, "Probing for a s5k83a sensor");
 
        /* Preinit the sensor */
        for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
@@ -51,10 +160,10 @@ int s5k83a_probe(struct sd *sd)
        /* We don't know what register (if any) that contain the product id
         * Just pick the first addresses that seem to produce the same results
         * on multiple machines */
-       if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1))
+       if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
                return -ENODEV;
 
-       if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1))
+       if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
                return -ENODEV;
 
        if ((prod_id == 0xff) || (ver_id == 0xff))
@@ -63,97 +172,38 @@ int s5k83a_probe(struct sd *sd)
                info("Detected a s5k83a sensor");
 
 sensor_found:
-       sd->gspca_dev.cam.cam_mode = s5k83a.modes;
-       sd->gspca_dev.cam.nmodes = s5k83a.nmodes;
-       sd->desc->ctrls = s5k83a.ctrls;
-       sd->desc->nctrls = s5k83a.nctrls;
-       return 0;
-}
-
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
-                      u8 *i2c_data, const u8 len)
-{
-       int err, i;
-
-       do {
-               err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
-       } while ((*i2c_data & I2C_BUSY) && !err);
-       if (err < 0)
-               goto out;
-
-       err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
-                                sd->sensor->i2c_slave_id);
-       if (err < 0)
-               goto out;
-
-       err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
-       if (err < 0)
-               goto out;
-
-       err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
-
-       do {
-               err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
-       } while ((*i2c_data & I2C_BUSY) && !err);
-
-       for (i = 0; i < len && !len; i++) {
-               err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
-               PDEBUG(D_CONF, "Reading sensor register "
-                                 "0x%x containing 0x%x ", address, *i2c_data);
-       }
-
-out:
-       return err;
-}
-
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
-                       u8 *i2c_data, const u8 len)
-{
-       int err, i;
-       u8 *p;
-       struct usb_device *udev = sd->gspca_dev.dev;
-       __u8 *buf = sd->gspca_dev.usb_buf;
-
-       /* No sensor with a data width larger than 16 bits has yet been seen */
-       if (len > 2 || !len)
-               return -EINVAL;
-
-       memcpy(buf, sensor_urb_skeleton,
-              sizeof(sensor_urb_skeleton));
-
-       buf[11] = sd->sensor->i2c_slave_id;
-       buf[15] = address;
-
-       /* Special case larger sensor writes */
-       p = buf + 16;
-
-       /* Copy a four byte write sequence for each byte to be written to */
-       for (i = 0; i < len; i++) {
-               memcpy(p, sensor_urb_skeleton + 16, 4);
-               p[3] = i2c_data[i];
-               p += 4;
-               PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
-                      address, i2c_data[i]);
+       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) {
+               kfree(sens_priv);
+               return -ENOMEM;
        }
 
-       /* Copy the tailer */
-       memcpy(p, sensor_urb_skeleton + 20, 4);
+       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);
 
-       /* Set the total length */
-       p[3] = 0x10 + len;
+       /* null the pointer! thread is't running now */
+       sens_priv->rotation_thread = NULL;
 
-       err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                             0x04, 0x40, 0x19,
-                             0x0000, buf,
-                             20 + len * 4, M5602_URB_MSG_TIMEOUT);
+       for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++)
+               sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value;
 
-       return (err < 0) ? err : 0;
+       sd->sensor_priv = sens_priv;
+       return 0;
 }
 
 int s5k83a_init(struct sd *sd)
 {
        int i, err = 0;
+       s32 *sensor_settings =
+                       ((struct s5k83a_priv *) sd->sensor_priv)->settings;
 
        for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
                u8 data[2] = {0x00, 0x00};
@@ -186,180 +236,216 @@ int s5k83a_init(struct sd *sd)
        if (dump_sensor)
                s5k83a_dump_registers(sd);
 
-       return (err < 0) ? err : 0;
+       err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+       if (err < 0)
+               return err;
+
+       err = s5k83a_set_brightness(&sd->gspca_dev,
+                                    sensor_settings[BRIGHTNESS_IDX]);
+       if (err < 0)
+               return err;
+
+       err = s5k83a_set_exposure(&sd->gspca_dev,
+                                  sensor_settings[EXPOSURE_IDX]);
+       if (err < 0)
+               return err;
+
+       err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
+       if (err < 0)
+               return err;
+
+       err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
+
+       return err;
 }
 
-int s5k83a_power_down(struct sd *sd)
+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;
 }
 
-void s5k83a_dump_registers(struct sd *sd)
+int s5k83a_start(struct sd *sd)
 {
-       int address;
-       u8 page, old_page;
-       s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+       int i, err = 0;
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       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;
-                       s5k83a_read_sensor(sd, address, &val, 1);
-                       info("register 0x%x contains 0x%x",
-                            address, val);
-               }
+       /* 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);
+
+       /* Preinit the sensor */
+       for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
+               u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
+               if (start_s5k83a[i][0] == SENSOR)
+                       err = m5602_write_sensor(sd, start_s5k83a[i][1],
+                               data, 2);
+               else
+                       err = m5602_write_bridge(sd, start_s5k83a[i][1],
+                               data[0]);
        }
-       info("s5k83a register state dump complete");
+       if (err < 0)
+               return err;
 
-       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;
+       return s5k83a_set_led_indication(sd, 1);
+}
 
-                       s5k83a_read_sensor(sd, address, &old_val, 1);
-                       m5602_write_sensor(sd, address, &test_val, 1);
-                       s5k83a_read_sensor(sd, address, &ctrl_val, 1);
+int s5k83a_stop(struct sd *sd)
+{
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-                       if (ctrl_val == test_val)
-                               info("register 0x%x is writeable", address);
-                       else
-                               info("register 0x%x is read only", address);
+       if (sens_priv->rotation_thread)
+               kthread_stop(sens_priv->rotation_thread);
 
-                       /* 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);
+       return s5k83a_set_led_indication(sd, 0);
 }
 
-int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+void s5k83a_disconnect(struct sd *sd)
 {
-       int err;
-       u8 data[2];
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct s5k83a_priv *sens_priv = sd->sensor_priv;
 
-       err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
-       if (err < 0)
-               goto out;
+       s5k83a_stop(sd);
 
-       data[1] = data[1] << 1;
-       *val = data[1];
+       sd->sensor = NULL;
+       kfree(sens_priv->settings);
+       kfree(sens_priv);
+}
 
-out:
-       return err;
+static int s5k83a_get_gain(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[GAIN_IDX];
+       return 0;
 }
 
-int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+static 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;
        err = m5602_write_sensor(sd, 0x14, data, 2);
        if (err < 0)
-               goto out;
+               return err;
 
        data[0] = 0x01;
        data[1] = 0x00;
        err = m5602_write_sensor(sd, 0x0d, data, 2);
        if (err < 0)
-               goto out;
+               return err;
 
        /* 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);
 
-out:
        return err;
 }
 
-int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
+static 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 = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
-       if (err < 0)
-               goto out;
-
-       *val = data;
-
-out:
-       return err;
+       *val = sens_priv->settings[BRIGHTNESS_IDX];
+       return 0;
 }
 
-int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
+static 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)
+static 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 = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2);
-       if (err < 0)
-               goto out;
-
-       data[1] = data[1] & 0x3f;
-       if (data[1] > S5K83A_MAXIMUM_GAIN)
-               data[1] = S5K83A_MAXIMUM_GAIN;
-
-       *val = data[1];
-
-out:
-       return err;
+       *val = sens_priv->settings[EXPOSURE_IDX];
+       return 0;
 }
 
-int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+static 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)
+static 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)
-               goto out;
-
-       err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
-       *val = (data[0] | 0x40) ? 1 : 0;
-
-out:
-       return err;
+       *val = sens_priv->settings[VFLIP_IDX];
+       return 0;
 }
 
-int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
+                               __s32 vflip, __s32 hflip)
 {
        int err;
        u8 data[1];
@@ -368,68 +454,149 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
        data[0] = 0x05;
        err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
        if (err < 0)
-               goto out;
+               return err;
 
-       err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
-       if (err < 0)
-               goto out;
+       /* 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)
-               goto out;
+               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;
 
-out:
+       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)
+static 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);
-       if (err < 0)
-               goto out;
+       sens_priv->settings[VFLIP_IDX] = val;
+
+       s5k83a_get_hflip(gspca_dev, &hflip);
 
-       err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
-       *val = (data[0] | 0x80) ? 1 : 0;
+       err = s5k83a_get_rotation(sd, &reg);
+       if (err < 0)
+               return err;
+       if (reg) {
+               val = !val;
+               hflip = !hflip;
+       }
 
-out:
+       err = s5k83a_set_flip_real(gspca_dev, val, hflip);
        return err;
 }
 
-int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+static 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;
+}
+
+static 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)
-               goto out;
+       sens_priv->settings[HFLIP_IDX] = val;
 
-       err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+       s5k83a_get_vflip(gspca_dev, &vflip);
+
+       err = s5k83a_get_rotation(sd, &reg);
        if (err < 0)
-               goto out;
+               return err;
+       if (reg) {
+               val = !val;
+               vflip = !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_set_flip_real(gspca_dev, vflip, val);
+       return err;
+}
+
+static int s5k83a_set_led_indication(struct sd *sd, u8 val)
+{
+       int err = 0;
+       u8 data[1];
+
+       err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
        if (err < 0)
-               goto out;
+               return err;
 
-       data[0] = (val) ? 0x0a : 0x0b;
-       err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
-out:
+       if (val)
+               data[0] = data[0] | S5K83A_GPIO_LED_MASK;
+       else
+               data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
+
+       err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
+
+       return err;
+}
+
+/* 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);
+}