[media] gscpa_ov534_9: Add support for ov3610 sensor
authorVladik Aranov <earnol@mail.ru>
Sat, 21 Sep 2013 00:09:44 +0000 (21:09 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Tue, 15 Oct 2013 15:49:51 +0000 (12:49 -0300)
Dear Hans de Goede, I have Ubuntu (raring) and recently bought digital
microscope eyepiece Lomo MD300. It is the same device as Future Optics MVV3000.
Unfortunately it does not work out of box. Moreover drivers refused to work
under Windows 7 as well leaving me only with Win Xp working system. I have had
no choice but to examine what happened in USB bus and attempt to reproduce the
sequence. So, i have download the source(3.8.0-30 kernel) and made required
changes to driver to make my hardware work.
I submit my changed files to you as maintainer of the driver so you can
integrade my changes into main stream. Thanx. Hopefully my Ubuntu will work
with my hardware out of box.
Sincerely yours Vladik Aranov

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
drivers/media/usb/gspca/ov534_9.c

index c4cd028fe0b4c8ea0c662ab82fbf98695f23a722..47085cf2d72365b5fdac410d5394bbdb8a7407b0 100644 (file)
@@ -59,6 +59,7 @@ enum sensors {
        SENSOR_OV965x,          /* ov9657 */
        SENSOR_OV971x,          /* ov9712 */
        SENSOR_OV562x,          /* ov5621 */
+       SENSOR_OV361x,          /* ov3610 */
        NSENSORS
 };
 
@@ -106,6 +107,274 @@ static const struct v4l2_pix_format ov562x_mode[] = {
        }
 };
 
+enum ov361x {
+       ov361x_2048 = 0,
+       ov361x_1600,
+       ov361x_1024,
+       ov361x_640,
+       ov361x_320,
+       ov361x_160,
+       ov361x_last
+};
+
+static const struct v4l2_pix_format ov361x_mode[] = {
+       {0x800, 0x600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 0x800,
+               .sizeimage = 0x800 * 0x600,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 1600,
+               .sizeimage = 1600 * 1200,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 768,
+               .sizeimage = 1024 * 768,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+       {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120,
+               .colorspace = V4L2_COLORSPACE_SRGB}
+};
+
+static const u8 ov361x_start_2048[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0c},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x00},
+       {0x17, 0x10},
+       {0x18, 0x90},
+       {0x19, 0x00},
+       {0x1a, 0xc0},
+};
+static const u8 ov361x_bridge_start_2048[][2] = {
+       {0xf1, 0x60},
+       {0x88, 0x00},
+       {0x89, 0x08},
+       {0x8a, 0x00},
+       {0x8b, 0x06},
+       {0x8c, 0x01},
+       {0x8d, 0x10},
+       {0x1c, 0x00},
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},
+       {0x1d, 0x2e},
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_1600[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x00},
+       {0x17, 0x10},
+       {0x18, 0x90},
+       {0x19, 0x00},
+       {0x1a, 0xc0},
+};
+static const u8 ov361x_bridge_start_1600[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x08},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x06},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_1024[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+static const u8 ov361x_bridge_start_1024[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input  */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_640[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_640[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0]*/
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_320[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_320[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer; */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
+static const u8 ov361x_start_160[][2] = {
+       {0x12, 0x80},
+       {0x13, 0xcf},
+       {0x14, 0x40},
+       {0x15, 0x00},
+       {0x01, 0x80},
+       {0x02, 0x80},
+       {0x04, 0x70},
+       {0x0d, 0x40},
+       {0x0f, 0x47},
+       {0x11, 0x81},
+       {0x32, 0x36},
+       {0x33, 0x0C},
+       {0x34, 0x00},
+       {0x35, 0x90},
+       {0x12, 0x40},
+       {0x17, 0x1f},
+       {0x18, 0x5f},
+       {0x19, 0x00},
+       {0x1a, 0x68},
+};
+
+static const u8 ov361x_bridge_start_160[][2] = {
+       {0xf1, 0x60},  /* Hsize[7:0] */
+       {0x88, 0x00},  /* Hsize[15:8] Write Only, can't read */
+       {0x89, 0x04},  /* Vsize[7:0] */
+       {0x8a, 0x00},  /* Vsize[15:8] Write Only, can't read */
+       {0x8b, 0x03},  /* for Iso */
+       {0x8c, 0x01},  /* RAW input */
+       {0x8d, 0x10},
+       {0x1c, 0x00},  /* RAW output, Iso transfer */
+       {0x1d, 0x48},
+       {0x1d, 0x00},
+       {0x1d, 0xff},
+       {0x1c, 0x0a},  /* turn off JPEG, Iso mode */
+       {0x1d, 0x2e},  /* for Iso */
+       {0x1d, 0x1e},
+};
+
 static const u8 bridge_init[][2] = {
        {0x88, 0xf8},
        {0x89, 0xff},
@@ -898,7 +1167,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev)
        int i;
 
        for (i = 0; i < 5; i++) {
-               msleep(10);
+               msleep(20);
                data = reg_r(gspca_dev, OV534_REG_STATUS);
 
                switch (data) {
@@ -1221,6 +1490,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
                sccb_w_array(gspca_dev, ov562x_init_2,
                                ARRAY_SIZE(ov562x_init_2));
                reg_w(gspca_dev, 0xe0, 0x00);
+       } else if ((sensor_id & 0xfff0) == 0x3610) {
+               sd->sensor = SENSOR_OV361x;
+               gspca_dev->cam.cam_mode = ov361x_mode;
+               gspca_dev->cam.nmodes = ARRAY_SIZE(ov361x_mode);
+               reg_w(gspca_dev, 0xe7, 0x3a);
+               reg_w(gspca_dev, 0xf1, 0x60);
+               sccb_write(gspca_dev, 0x12, 0x80);
        } else {
                pr_err("Unknown sensor %04x", sensor_id);
                return -EINVAL;
@@ -1229,6 +1505,53 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return gspca_dev->usb_err;
 }
 
+static int sd_start_ov361x(struct gspca_dev *gspca_dev)
+{
+       sccb_write(gspca_dev, 0x12, 0x80);
+       msleep(20);
+       switch (gspca_dev->curr_mode % (ov361x_last)) {
+       case ov361x_2048:
+               reg_w_array(gspca_dev, ov361x_bridge_start_2048,
+                           ARRAY_SIZE(ov361x_bridge_start_2048));
+               sccb_w_array(gspca_dev, ov361x_start_2048,
+                            ARRAY_SIZE(ov361x_start_2048));
+               break;
+       case ov361x_1600:
+               reg_w_array(gspca_dev, ov361x_bridge_start_1600,
+                           ARRAY_SIZE(ov361x_bridge_start_1600));
+               sccb_w_array(gspca_dev, ov361x_start_1600,
+                            ARRAY_SIZE(ov361x_start_1600));
+               break;
+       case ov361x_1024:
+               reg_w_array(gspca_dev, ov361x_bridge_start_1024,
+                           ARRAY_SIZE(ov361x_bridge_start_1024));
+               sccb_w_array(gspca_dev, ov361x_start_1024,
+                            ARRAY_SIZE(ov361x_start_1024));
+               break;
+       case ov361x_640:
+               reg_w_array(gspca_dev, ov361x_bridge_start_640,
+                           ARRAY_SIZE(ov361x_bridge_start_640));
+               sccb_w_array(gspca_dev, ov361x_start_640,
+                            ARRAY_SIZE(ov361x_start_640));
+               break;
+       case ov361x_320:
+               reg_w_array(gspca_dev, ov361x_bridge_start_320,
+                           ARRAY_SIZE(ov361x_bridge_start_320));
+               sccb_w_array(gspca_dev, ov361x_start_320,
+                            ARRAY_SIZE(ov361x_start_320));
+               break;
+       case ov361x_160:
+               reg_w_array(gspca_dev, ov361x_bridge_start_160,
+                           ARRAY_SIZE(ov361x_bridge_start_160));
+               sccb_w_array(gspca_dev, ov361x_start_160,
+                            ARRAY_SIZE(ov361x_start_160));
+               break;
+       }
+       reg_w(gspca_dev, 0xe0, 0x00); /* start transfer */
+
+       return gspca_dev->usb_err;
+}
+
 static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -1237,6 +1560,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
                return gspca_dev->usb_err;
        if (sd->sensor == SENSOR_OV562x)
                return gspca_dev->usb_err;
+       if (sd->sensor == SENSOR_OV361x)
+               return sd_start_ov361x(gspca_dev);
 
        switch (gspca_dev->curr_mode) {
        case QVGA_MODE:                 /* 320x240 */
@@ -1290,6 +1615,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
+       if (((struct sd *)gspca_dev)->sensor == SENSOR_OV361x) {
+               reg_w(gspca_dev, 0xe0, 0x01); /* stop transfer */
+               /* reg_w(gspca_dev, 0x31, 0x09); */
+               return;
+       }
        reg_w(gspca_dev, 0xe0, 0x01);
        set_led(gspca_dev, 0);
        reg_w(gspca_dev, 0xe0, 0x00);
@@ -1425,6 +1755,8 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
 
        if (sd->sensor == SENSOR_OV971x)
                return 0;
+       if (sd->sensor == SENSOR_OV361x)
+               return 0;
        gspca_dev->vdev.ctrl_handler = hdl;
        v4l2_ctrl_handler_init(hdl, 7);
        if (sd->sensor == SENSOR_OV562x) {