V4L/DVB: gspca_sonixb: pas106: fixup bright ctrl and add gain and exposure ctrls

Fixup brightness ctrl and add gain and exposure ctrls for PAS106B sensors,
this allows enabling autogain (done), and makes the cam usable in low light
conditions.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 81a7dd6..43a7721 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -188,8 +188,8 @@
 			.id = V4L2_CID_EXPOSURE,
 			.type = V4L2_CTRL_TYPE_INTEGER,
 			.name = "Exposure",
-#define EXPOSURE_DEF  66 /*  33 ms / 30 fps (except on PAS202) */
-#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PAS202) */
+#define EXPOSURE_DEF  66 /*  33 ms / 30 fps (except on PASXXX) */
+#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */
 			.minimum = 0,
 			.maximum = 1023,
 			.step = 1,
@@ -408,6 +408,30 @@
 	0x18, 0x10, 0x02, 0x02, 0x09, 0x07
 };
 /* compression 0x86 mckinit1 0x2b */
+
+/* "Known" PAS106B registers:
+  0x02 clock divider
+  0x03 Variable framerate bits 4-11
+  0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !!
+       The variable framerate control must never be set lower then 300,
+       which sets the framerate at 90 / reg02, otherwise vsync is lost.
+  0x05 Shutter Time Line Offset, this can be used as an exposure control:
+       0 = use full frame time, 255 = no exposure at all
+       Note this may never be larger then "var-framerate control" / 2 - 2.
+       When var-framerate control is < 514, no exposure is reached at the max
+       allowed value for the framerate control value, rather then at 255.
+  0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but
+       only a very little bit, leave at 0xcd
+  0x07 offset sign bit (bit0 1 > negative offset)
+  0x08 offset
+  0x09 Blue Gain
+  0x0a Green1 Gain
+  0x0b Green2 Gain
+  0x0c Red Gain
+  0x0e Global gain
+  0x13 Write 1 to commit settings to sensor
+*/
+
 static const __u8 pas106_sensor_init[][8] = {
 	/* Pixel Clock Divider 6 */
 	{ 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 },
@@ -532,7 +556,7 @@
 SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60),
 SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3,
 	F_GAIN, 0, 0x21),
-SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ,
+SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_GAIN|F_SIF, NO_FREQ,
 	0),
 SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN,
 	NO_FREQ, 0),
@@ -628,26 +652,19 @@
 			goto err;
 		break;
 	    }
-	case SENSOR_PAS106: {
-		__u8 i2c1[] =
-			{0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14};
-
-		i2c1[3] = sd->brightness >> 3;
-		i2c1[2] = 0x0e;
-		if (i2c_w(gspca_dev, i2c1) < 0)
-			goto err;
-		i2c1[3] = 0x01;
-		i2c1[2] = 0x13;
-		if (i2c_w(gspca_dev, i2c1) < 0)
-			goto err;
-		break;
-	    }
+	case SENSOR_PAS106:
 	case SENSOR_PAS202: {
 		__u8 i2cpbright[] =
 			{0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16};
-		const __u8 i2cpdoit[] =
+		__u8 i2cpdoit[] =
 			{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
 
+		/* PAS106 uses reg 7 and 8 instead of b and c */
+		if (sd->sensor == SENSOR_PAS106) {
+			i2cpbright[2] = 7;
+			i2cpdoit[2] = 0x13;
+		}
+
 		if (sd->brightness < 127) {
 			/* change reg 0x0b, signreg */
 			i2cpbright[3] = 0x01;
@@ -709,18 +726,28 @@
 			goto err;
 		break;
 	    }
+	case SENSOR_PAS106:
 	case SENSOR_PAS202: {
 		__u8 i2cpgain[] =
-			{0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x63, 0x15};
+			{0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15};
 		__u8 i2cpcolorgain[] =
 			{0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15};
-		const __u8 i2cpdoit[] =
-			{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x63, 0x16};
+		__u8 i2cpdoit[] =
+			{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+		/* PAS106 uses different regs (and has split green gains) */
+		if (sd->sensor == SENSOR_PAS106) {
+			i2cpgain[2] = 0x0e;
+			i2cpcolorgain[0] = 0xd0;
+			i2cpcolorgain[2] = 0x09;
+			i2cpdoit[2] = 0x13;
+		}
 
 		i2cpgain[3] = sd->gain >> 3;
 		i2cpcolorgain[3] = sd->gain >> 4;
 		i2cpcolorgain[4] = sd->gain >> 4;
 		i2cpcolorgain[5] = sd->gain >> 4;
+		i2cpcolorgain[6] = sd->gain >> 4;
 
 		if (i2c_w(gspca_dev, i2cpgain) < 0)
 			goto err;
@@ -883,6 +910,38 @@
 			goto err;
 		break;
 	    }
+	case SENSOR_PAS106: {
+		__u8 i2cpframerate[] =
+			{0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
+		__u8 i2cpexpo[] =
+			{0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14};
+		const __u8 i2cpdoit[] =
+			{0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14};
+		int framerate_ctrl;
+
+		/* For values below 150 use partial frame exposure, above
+		   that use framerate ctrl */
+		if (sd->exposure < 150) {
+			i2cpexpo[3] = 150 - sd->exposure;
+			framerate_ctrl = 300;
+		} else {
+			/* The PAS106's exposure control goes from 0 - 4095,
+			   but anything below 300 causes vsync issues, so scale
+			   our 150-1023 to 300-4095 */
+			framerate_ctrl = (sd->exposure - 150) * 1000 / 230 +
+					 300;
+		}
+
+		i2cpframerate[3] = framerate_ctrl >> 4;
+		i2cpframerate[4] = framerate_ctrl & 0x0f;
+		if (i2c_w(gspca_dev, i2cpframerate) < 0)
+			goto err;
+		if (i2c_w(gspca_dev, i2cpexpo) < 0)
+			goto err;
+		if (i2c_w(gspca_dev, i2cpdoit) < 0)
+			goto err;
+		break;
+	    }
 	}
 	return;
 err: