V4L/DVB: gspca - sonixj: Add sensor soi768

The webcams 0c45:613e may contain the sensors ov7630 or soi768.
A sensor probe is done at init time when the sensor is declared ov7630.

Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 19ae4f5..bb923ef 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -80,6 +80,7 @@
 	SENSOR_OV7660,
 	SENSOR_PO1030,
 	SENSOR_PO2030N,
+	SENSOR_SOI768,
 	SENSOR_SP80708,
 } sensors;
 	u8 i2c_addr;
@@ -330,6 +331,10 @@
 			(1 << INFRARED_IDX) |
 			(1 << VFLIP_IDX) |
 			(1 << FREQ_IDX),
+[SENSOR_SOI768] =	(1 << AUTOGAIN_IDX) |
+			(1 << INFRARED_IDX) |
+			(1 << VFLIP_IDX) |
+			(1 << FREQ_IDX),
 
 [SENSOR_SP80708] =	(1 << AUTOGAIN_IDX) |
 			(1 << INFRARED_IDX) |
@@ -495,6 +500,17 @@
 	0x07,	0x00,	0x00,	0x00
 };
 
+static const u8 sn_soi768[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x21,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x21,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x01,	0x08,	0x28,	0x1e,	0x00,
+/*	reg18	reg19	reg1a	reg1b */
+	0x07,	0x00,	0x00,	0x00
+};
+
 static const u8 sn_sp80708[0x1c] = {
 /*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
 	0x00,	0x63,	0x60,	0x00,	0x1a,	0x20,	0x20,	0x20,
@@ -520,7 +536,8 @@
 [SENSOR_OV7660] =	sn_ov7660,
 [SENSOR_PO1030] =	sn_po1030,
 [SENSOR_PO2030N] =	sn_po2030n,
-[SENSOR_SP80708] =	sn_sp80708
+[SENSOR_SOI768] =	sn_soi768,
+[SENSOR_SP80708] =	sn_sp80708,
 };
 
 /* default gamma table */
@@ -1173,6 +1190,37 @@
 	{}
 };
 
+static const u8 soi768_sensor_init[][8] = {
+	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+	{0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
+	{0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{}
+};
+static const u8 soi768_sensor_param1[][8] = {
+	{0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10},
+/* */
+/*	{0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+/*	{0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */
+	{0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+/*	{0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+	{0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10},
+/* the next sequence should be used for auto gain */
+	{0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10},
+			/* global gain ? : 07 - change with 0x15 at the end */
+	{0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
+	{0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x21, 0x2d, 0x00, 0x02, 0x00, 0x00, 0x10},
+			/* exposure ? : 0200 - change with 0x1e at the end */
+	{}
+};
+
 static const u8 sp80708_sensor_init[][8] = {
 	{0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
@@ -1271,6 +1319,7 @@
 [SENSOR_OV7660] =	ov7660_sensor_init,
 [SENSOR_PO1030] =	po1030_sensor_init,
 [SENSOR_PO2030N] =	po2030n_sensor_init,
+[SENSOR_SOI768] =	soi768_sensor_init,
 [SENSOR_SP80708] =	sp80708_sensor_init,
 };
 
@@ -1495,6 +1544,30 @@
 	}
 }
 
+static void ov7630_probe(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 val;
+
+	/* check ov76xx */
+	reg_w1(gspca_dev, 0x17, 0x62);
+	reg_w1(gspca_dev, 0x01, 0x08);
+	sd->i2c_addr = 0x21;
+	i2c_r(gspca_dev, 0x0a, 2);
+	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+	reg_w1(gspca_dev, 0x01, 0x29);
+	reg_w1(gspca_dev, 0x17, 0x42);
+	if (val == 0x7628) {			/* soi768 */
+		sd->sensor = SENSOR_SOI768;
+/*fixme: only valid for 0c45:613e?*/
+		gspca_dev->cam.input_flags =
+				V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+		PDEBUG(D_PROBE, "Sensor soi768");
+		return;
+	}
+	PDEBUG(D_PROBE, "Sensor ov%04x", val);
+}
+
 static void ov7648_probe(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
@@ -1591,6 +1664,7 @@
 	case SENSOR_OV7660:
 	case SENSOR_PO1030:
 	case SENSOR_PO2030N:
+	case SENSOR_SOI768:
 	case SENSOR_SP80708:
 		reg9a = reg9a_spec;
 		break;
@@ -1656,6 +1730,7 @@
 		reg_w1(gspca_dev, 0x01, 0x42);
 		break;
 	case SENSOR_PO1030:
+	case SENSOR_SOI768:
 		reg_w1(gspca_dev, 0x01, 0x61);
 		reg_w1(gspca_dev, 0x17, 0x20);
 		reg_w1(gspca_dev, 0x01, 0x60);
@@ -1771,6 +1846,9 @@
 		case SENSOR_MI0360:
 			mi0360_probe(gspca_dev);
 			break;
+		case SENSOR_OV7630:
+			ov7630_probe(gspca_dev);
+			break;
 		case SENSOR_OV7648:
 			ov7648_probe(gspca_dev);
 			break;
@@ -2280,6 +2358,7 @@
 		reg17 = 0x20;
 		break;
 	case SENSOR_OV7660:
+	case SENSOR_SOI768:
 		reg17 = 0xa0;
 		break;
 	case SENSOR_PO1030:
@@ -2317,6 +2396,7 @@
 		reg_w1(gspca_dev, 0x9a, 0x0a);
 		break;
 	case SENSOR_PO2030N:
+	case SENSOR_SOI768:
 		reg_w1(gspca_dev, 0x9a, 0x06);
 		break;
 	default:
@@ -2403,6 +2483,11 @@
 		reg1 = 0x46;
 		reg17 = 0xa2;
 		break;
+	case SENSOR_SOI768:
+		init = soi768_sensor_param1;
+		reg1 = 0x44;
+		reg17 = 0xa2;
+		break;
 	default:
 /*	case SENSOR_SP80708: */
 		init = sp80708_sensor_param1;
@@ -2425,6 +2510,7 @@
 	switch (sd->sensor) {
 	case SENSOR_ADCM1700:
 	case SENSOR_GC0307:
+	case SENSOR_SOI768:
 		reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
 		break;
 	case SENSOR_PO2030N:
@@ -2439,6 +2525,7 @@
 	case SENSOR_OV7630:
 	case SENSOR_OV7648:
 	case SENSOR_OV7660:
+	case SENSOR_SOI768:
 		reg_w(gspca_dev, 0xce, CE_ov76xx, 4);
 		break;
 	case SENSOR_GC0307:
@@ -2480,6 +2567,8 @@
 		{ 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
 	static const u8 stopov7648[] =
 		{ 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
+	static const u8 stopsoi768[] =
+		{ 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 };
 	u8 data;
 	const u8 *sn9c1xx;
 
@@ -2504,6 +2593,10 @@
 	case SENSOR_PO1030:
 		data = 0x29;
 		break;
+	case SENSOR_SOI768:
+		i2c_w8(gspca_dev, stopsoi768);
+		data = 0x29;
+		break;
 	}
 	sn9c1xx = sn_tb[sd->sensor];
 	reg_w1(gspca_dev, 0x01, sn9c1xx[1]);