V4L/DVB: gspca - zc3xx: Add back the brightness control

This patch also changes a bit the contrast control.

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/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 73c4ebb..1420eb2 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -40,6 +40,7 @@
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
 
+	u8 brightness;
 	u8 contrast;
 	u8 gamma;
 	u8 autogain;
@@ -79,6 +80,8 @@
 };
 
 /* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
@@ -93,6 +96,20 @@
 static const struct ctrl sd_ctrls[] = {
 	{
 	    {
+		.id      = V4L2_CID_BRIGHTNESS,
+		.type    = V4L2_CTRL_TYPE_INTEGER,
+		.name    = "Brightness",
+		.minimum = 0,
+		.maximum = 255,
+		.step    = 1,
+#define BRIGHTNESS_DEF 128
+		.default_value = BRIGHTNESS_DEF,
+	    },
+	    .set = sd_setbrightness,
+	    .get = sd_getbrightness,
+	},
+	{
+	    {
 		.id      = V4L2_CID_CONTRAST,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
 		.name    = "Contrast",
@@ -132,7 +149,7 @@
 	    .set = sd_setautogain,
 	    .get = sd_getautogain,
 	},
-#define LIGHTFREQ_IDX 3
+#define LIGHTFREQ_IDX 4
 	{
 	    {
 		.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -6011,9 +6028,12 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	struct usb_device *dev = gspca_dev->dev;
 	const u8 *Tgamma;
-	int g, i, k, adj, gp;
+	int g, i, brightness, contrast, adj, gp1, gp2;
 	u8 gr[16];
-	static const u8 delta_tb[16] =		/* delta for contrast */
+	static const u8 delta_b[16] =		/* delta for brightness */
+		{0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
+		 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18};
+	static const u8 delta_c[16] =		/* delta for contrast */
 		{0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06,
 		 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02};
 	static const u8 gamma_tb[6][16] = {
@@ -6033,30 +6053,30 @@
 
 	Tgamma = gamma_tb[sd->gamma - 1];
 
-	k = ((int) sd->contrast - 128);		/* -128 / 128 */
+	contrast = ((int) sd->contrast - 128);		/* -128 / 127 */
+	brightness = ((int) sd->brightness - 128);	/* -128 / 92 */
 	adj = 0;
-	gp = 0;
+	gp1 = gp2 = 0;
 	for (i = 0; i < 16; i++) {
-		g = Tgamma[i] - delta_tb[i] * k / 256 - adj / 2;
+		g = Tgamma[i] + delta_b[i] * brightness / 256
+				- delta_c[i] * contrast / 256 - adj / 2;
 		if (g > 0xff)
 			g = 0xff;
 		else if (g < 0)
 			g = 0;
 		reg_w(dev, g, 0x0120 + i);	/* gamma */
-		if (k > 0)
+		if (contrast > 0)
 			adj--;
-		else
+		else if (contrast < 0)
 			adj++;
-
-		if (i != 0) {
-			if (gp == 0)
-				gr[i - 1] = 0;
-			else
-				gr[i - 1] = g - gp;
-		}
-		gp = g;
+		if (i > 1)
+			gr[i - 1] = (g - gp2) / 2;
+		else if (i != 0)
+			gr[0] = gp1 == 0 ? 0 : (g - gp1);
+		gp2 = gp1;
+		gp1 = g;
 	}
-	gr[15] = gr[14] / 2;
+	gr[15] = (0xff - gp2) / 2;
 	for (i = 0; i < 16; i++)
 		reg_w(dev, gr[i], 0x0130 + i);	/* gradient */
 }
@@ -6744,6 +6764,7 @@
 		cam->nmodes = ARRAY_SIZE(broken_vga_mode);
 		break;
 	}
+	sd->brightness = BRIGHTNESS_DEF;
 	sd->contrast = CONTRAST_DEF;
 	sd->gamma = gamma[sd->sensor];
 	sd->autogain = AUTOGAIN_DEF;
@@ -6954,6 +6975,24 @@
 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->brightness = val;
+	if (gspca_dev->streaming)
+		setcontrast(gspca_dev);
+	return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->brightness;
+	return 0;
+}
+
 static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;