xref: /openbmc/linux/drivers/media/usb/gspca/t613.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * T613 subdriver
40c0d06caSMauro Carvalho Chehab  *
50c0d06caSMauro Carvalho Chehab  * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr)
60c0d06caSMauro Carvalho Chehab  *
70c0d06caSMauro Carvalho Chehab  *Notes: * t613  + tas5130A
80c0d06caSMauro Carvalho Chehab  *	* Focus to light do not balance well as in win.
90c0d06caSMauro Carvalho Chehab  *	  Quality in win is not good, but its kinda better.
100c0d06caSMauro Carvalho Chehab  *	 * Fix some "extraneous bytes", most of apps will show the image anyway
110c0d06caSMauro Carvalho Chehab  *	 * Gamma table, is there, but its really doing something?
120c0d06caSMauro Carvalho Chehab  *	 * 7~8 Fps, its ok, max on win its 10.
130c0d06caSMauro Carvalho Chehab  *			Costantino Leandro
140c0d06caSMauro Carvalho Chehab  */
150c0d06caSMauro Carvalho Chehab 
160c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
170c0d06caSMauro Carvalho Chehab 
180c0d06caSMauro Carvalho Chehab #define MODULE_NAME "t613"
190c0d06caSMauro Carvalho Chehab 
200c0d06caSMauro Carvalho Chehab #include <linux/input.h>
210c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
220c0d06caSMauro Carvalho Chehab #include "gspca.h"
230c0d06caSMauro Carvalho Chehab 
240c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>");
250c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver");
260c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
270c0d06caSMauro Carvalho Chehab 
280c0d06caSMauro Carvalho Chehab struct sd {
290c0d06caSMauro Carvalho Chehab 	struct gspca_dev gspca_dev;	/* !! must be the first item */
300c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl *freq;
310c0d06caSMauro Carvalho Chehab 	struct { /* awb / color gains control cluster */
320c0d06caSMauro Carvalho Chehab 		struct v4l2_ctrl *awb;
330c0d06caSMauro Carvalho Chehab 		struct v4l2_ctrl *gain;
340c0d06caSMauro Carvalho Chehab 		struct v4l2_ctrl *red_balance;
350c0d06caSMauro Carvalho Chehab 		struct v4l2_ctrl *blue_balance;
360c0d06caSMauro Carvalho Chehab 	};
370c0d06caSMauro Carvalho Chehab 
380c0d06caSMauro Carvalho Chehab 	u8 sensor;
390c0d06caSMauro Carvalho Chehab 	u8 button_pressed;
400c0d06caSMauro Carvalho Chehab };
410c0d06caSMauro Carvalho Chehab enum sensors {
420c0d06caSMauro Carvalho Chehab 	SENSOR_OM6802,
430c0d06caSMauro Carvalho Chehab 	SENSOR_OTHER,
440c0d06caSMauro Carvalho Chehab 	SENSOR_TAS5130A,
450c0d06caSMauro Carvalho Chehab 	SENSOR_LT168G,		/* must verify if this is the actual model */
460c0d06caSMauro Carvalho Chehab };
470c0d06caSMauro Carvalho Chehab 
480c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format vga_mode_t16[] = {
490c0d06caSMauro Carvalho Chehab 	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
500c0d06caSMauro Carvalho Chehab 		.bytesperline = 160,
510c0d06caSMauro Carvalho Chehab 		.sizeimage = 160 * 120 * 4 / 8 + 590,
520c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
530c0d06caSMauro Carvalho Chehab 		.priv = 4},
540c0d06caSMauro Carvalho Chehab #if 0 /* HDG: broken with my test cam, so lets disable it */
550c0d06caSMauro Carvalho Chehab 	{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
560c0d06caSMauro Carvalho Chehab 		.bytesperline = 176,
570c0d06caSMauro Carvalho Chehab 		.sizeimage = 176 * 144 * 3 / 8 + 590,
580c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
590c0d06caSMauro Carvalho Chehab 		.priv = 3},
600c0d06caSMauro Carvalho Chehab #endif
610c0d06caSMauro Carvalho Chehab 	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
620c0d06caSMauro Carvalho Chehab 		.bytesperline = 320,
630c0d06caSMauro Carvalho Chehab 		.sizeimage = 320 * 240 * 3 / 8 + 590,
640c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
650c0d06caSMauro Carvalho Chehab 		.priv = 2},
660c0d06caSMauro Carvalho Chehab #if 0 /* HDG: broken with my test cam, so lets disable it */
670c0d06caSMauro Carvalho Chehab 	{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
680c0d06caSMauro Carvalho Chehab 		.bytesperline = 352,
690c0d06caSMauro Carvalho Chehab 		.sizeimage = 352 * 288 * 3 / 8 + 590,
700c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
710c0d06caSMauro Carvalho Chehab 		.priv = 1},
720c0d06caSMauro Carvalho Chehab #endif
730c0d06caSMauro Carvalho Chehab 	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
740c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
750c0d06caSMauro Carvalho Chehab 		.sizeimage = 640 * 480 * 3 / 8 + 590,
760c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
770c0d06caSMauro Carvalho Chehab 		.priv = 0},
780c0d06caSMauro Carvalho Chehab };
790c0d06caSMauro Carvalho Chehab 
800c0d06caSMauro Carvalho Chehab /* sensor specific data */
810c0d06caSMauro Carvalho Chehab struct additional_sensor_data {
820c0d06caSMauro Carvalho Chehab 	const u8 n3[6];
830c0d06caSMauro Carvalho Chehab 	const u8 *n4, n4sz;
840c0d06caSMauro Carvalho Chehab 	const u8 reg80, reg8e;
850c0d06caSMauro Carvalho Chehab 	const u8 nset8[6];
860c0d06caSMauro Carvalho Chehab 	const u8 data1[10];
870c0d06caSMauro Carvalho Chehab 	const u8 data2[9];
880c0d06caSMauro Carvalho Chehab 	const u8 data3[9];
890c0d06caSMauro Carvalho Chehab 	const u8 data5[6];
900c0d06caSMauro Carvalho Chehab 	const u8 stream[4];
910c0d06caSMauro Carvalho Chehab };
920c0d06caSMauro Carvalho Chehab 
930c0d06caSMauro Carvalho Chehab static const u8 n4_om6802[] = {
940c0d06caSMauro Carvalho Chehab 	0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c,
950c0d06caSMauro Carvalho Chehab 	0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68,
960c0d06caSMauro Carvalho Chehab 	0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1,
970c0d06caSMauro Carvalho Chehab 	0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8,
980c0d06caSMauro Carvalho Chehab 	0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48,
990c0d06caSMauro Carvalho Chehab 	0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0,
1000c0d06caSMauro Carvalho Chehab 	0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68,
1010c0d06caSMauro Carvalho Chehab 	0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40,
1020c0d06caSMauro Carvalho Chehab 	0xac, 0x84, 0xad, 0x86, 0xaf, 0x46
1030c0d06caSMauro Carvalho Chehab };
1040c0d06caSMauro Carvalho Chehab static const u8 n4_other[] = {
1050c0d06caSMauro Carvalho Chehab 	0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69,
1060c0d06caSMauro Carvalho Chehab 	0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68,
1070c0d06caSMauro Carvalho Chehab 	0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8,
1080c0d06caSMauro Carvalho Chehab 	0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8,
1090c0d06caSMauro Carvalho Chehab 	0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56,
1100c0d06caSMauro Carvalho Chehab 	0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5,
1110c0d06caSMauro Carvalho Chehab 	0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0,
1120c0d06caSMauro Carvalho Chehab 	0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00
1130c0d06caSMauro Carvalho Chehab };
1140c0d06caSMauro Carvalho Chehab static const u8 n4_tas5130a[] = {
1150c0d06caSMauro Carvalho Chehab 	0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20,
1160c0d06caSMauro Carvalho Chehab 	0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4,
1170c0d06caSMauro Carvalho Chehab 	0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10,
1180c0d06caSMauro Carvalho Chehab 	0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08,
1190c0d06caSMauro Carvalho Chehab 	0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a,
1200c0d06caSMauro Carvalho Chehab 	0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8,
1210c0d06caSMauro Carvalho Chehab 	0xc6, 0xda
1220c0d06caSMauro Carvalho Chehab };
1230c0d06caSMauro Carvalho Chehab static const u8 n4_lt168g[] = {
1240c0d06caSMauro Carvalho Chehab 	0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28,
1250c0d06caSMauro Carvalho Chehab 	0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70,
1260c0d06caSMauro Carvalho Chehab 	0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3,
1270c0d06caSMauro Carvalho Chehab 	0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20,
1280c0d06caSMauro Carvalho Chehab 	0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68,
1290c0d06caSMauro Carvalho Chehab 	0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40,
1300c0d06caSMauro Carvalho Chehab 	0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0,
1310c0d06caSMauro Carvalho Chehab 	0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c,
1320c0d06caSMauro Carvalho Chehab 	0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80
1330c0d06caSMauro Carvalho Chehab };
1340c0d06caSMauro Carvalho Chehab 
1350c0d06caSMauro Carvalho Chehab static const struct additional_sensor_data sensor_data[] = {
1360c0d06caSMauro Carvalho Chehab [SENSOR_OM6802] = {
1370c0d06caSMauro Carvalho Chehab 	.n3 =
1380c0d06caSMauro Carvalho Chehab 		{0x61, 0x68, 0x65, 0x0a, 0x60, 0x04},
1390c0d06caSMauro Carvalho Chehab 	.n4 = n4_om6802,
1400c0d06caSMauro Carvalho Chehab 	.n4sz = sizeof n4_om6802,
1410c0d06caSMauro Carvalho Chehab 	.reg80 = 0x3c,
1420c0d06caSMauro Carvalho Chehab 	.reg8e = 0x33,
1430c0d06caSMauro Carvalho Chehab 	.nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00},
1440c0d06caSMauro Carvalho Chehab 	.data1 =
1450c0d06caSMauro Carvalho Chehab 		{0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06,
1460c0d06caSMauro Carvalho Chehab 		 0xb3, 0xfc},
1470c0d06caSMauro Carvalho Chehab 	.data2 =
1480c0d06caSMauro Carvalho Chehab 		{0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
1490c0d06caSMauro Carvalho Chehab 		 0xff},
1500c0d06caSMauro Carvalho Chehab 	.data3 =
1510c0d06caSMauro Carvalho Chehab 		{0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
1520c0d06caSMauro Carvalho Chehab 		 0xff},
1530c0d06caSMauro Carvalho Chehab 	.data5 =	/* this could be removed later */
1540c0d06caSMauro Carvalho Chehab 		{0x0c, 0x03, 0xab, 0x13, 0x81, 0x23},
1550c0d06caSMauro Carvalho Chehab 	.stream =
1560c0d06caSMauro Carvalho Chehab 		{0x0b, 0x04, 0x0a, 0x78},
1570c0d06caSMauro Carvalho Chehab     },
1580c0d06caSMauro Carvalho Chehab [SENSOR_OTHER] = {
1590c0d06caSMauro Carvalho Chehab 	.n3 =
1600c0d06caSMauro Carvalho Chehab 		{0x61, 0xc2, 0x65, 0x88, 0x60, 0x00},
1610c0d06caSMauro Carvalho Chehab 	.n4 = n4_other,
1620c0d06caSMauro Carvalho Chehab 	.n4sz = sizeof n4_other,
1630c0d06caSMauro Carvalho Chehab 	.reg80 = 0xac,
1640c0d06caSMauro Carvalho Chehab 	.reg8e = 0xb8,
1650c0d06caSMauro Carvalho Chehab 	.nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00},
1660c0d06caSMauro Carvalho Chehab 	.data1 =
1670c0d06caSMauro Carvalho Chehab 		{0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a,
1680c0d06caSMauro Carvalho Chehab 		 0xe8, 0xfc},
1690c0d06caSMauro Carvalho Chehab 	.data2 =
1700c0d06caSMauro Carvalho Chehab 		{0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
1710c0d06caSMauro Carvalho Chehab 		 0xd9},
1720c0d06caSMauro Carvalho Chehab 	.data3 =
1730c0d06caSMauro Carvalho Chehab 		{0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
1740c0d06caSMauro Carvalho Chehab 		 0xd9},
1750c0d06caSMauro Carvalho Chehab 	.data5 =
1760c0d06caSMauro Carvalho Chehab 		{0x0c, 0x03, 0xab, 0x29, 0x81, 0x69},
1770c0d06caSMauro Carvalho Chehab 	.stream =
1780c0d06caSMauro Carvalho Chehab 		{0x0b, 0x04, 0x0a, 0x00},
1790c0d06caSMauro Carvalho Chehab     },
1800c0d06caSMauro Carvalho Chehab [SENSOR_TAS5130A] = {
1810c0d06caSMauro Carvalho Chehab 	.n3 =
1820c0d06caSMauro Carvalho Chehab 		{0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08},
1830c0d06caSMauro Carvalho Chehab 	.n4 = n4_tas5130a,
1840c0d06caSMauro Carvalho Chehab 	.n4sz = sizeof n4_tas5130a,
1850c0d06caSMauro Carvalho Chehab 	.reg80 = 0x3c,
1860c0d06caSMauro Carvalho Chehab 	.reg8e = 0xb4,
1870c0d06caSMauro Carvalho Chehab 	.nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00},
1880c0d06caSMauro Carvalho Chehab 	.data1 =
1890c0d06caSMauro Carvalho Chehab 		{0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27,
1900c0d06caSMauro Carvalho Chehab 		 0xc8, 0xfc},
1910c0d06caSMauro Carvalho Chehab 	.data2 =
1920c0d06caSMauro Carvalho Chehab 		{0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
1930c0d06caSMauro Carvalho Chehab 		 0xe0},
1940c0d06caSMauro Carvalho Chehab 	.data3 =
1950c0d06caSMauro Carvalho Chehab 		{0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
1960c0d06caSMauro Carvalho Chehab 		 0xe0},
1970c0d06caSMauro Carvalho Chehab 	.data5 =
1980c0d06caSMauro Carvalho Chehab 		{0x0c, 0x03, 0xab, 0x10, 0x81, 0x20},
1990c0d06caSMauro Carvalho Chehab 	.stream =
2000c0d06caSMauro Carvalho Chehab 		{0x0b, 0x04, 0x0a, 0x40},
2010c0d06caSMauro Carvalho Chehab     },
2020c0d06caSMauro Carvalho Chehab [SENSOR_LT168G] = {
2030c0d06caSMauro Carvalho Chehab 	.n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00},
2040c0d06caSMauro Carvalho Chehab 	.n4 = n4_lt168g,
2050c0d06caSMauro Carvalho Chehab 	.n4sz = sizeof n4_lt168g,
2060c0d06caSMauro Carvalho Chehab 	.reg80 = 0x7c,
2070c0d06caSMauro Carvalho Chehab 	.reg8e = 0xb3,
2080c0d06caSMauro Carvalho Chehab 	.nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00},
2090c0d06caSMauro Carvalho Chehab 	.data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40,
2100c0d06caSMauro Carvalho Chehab 		 0xb0, 0xf4},
2110c0d06caSMauro Carvalho Chehab 	.data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
2120c0d06caSMauro Carvalho Chehab 		 0xff},
2130c0d06caSMauro Carvalho Chehab 	.data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
2140c0d06caSMauro Carvalho Chehab 		 0xff},
2150c0d06caSMauro Carvalho Chehab 	.data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b},
2160c0d06caSMauro Carvalho Chehab 	.stream = {0x0b, 0x04, 0x0a, 0x28},
2170c0d06caSMauro Carvalho Chehab     },
2180c0d06caSMauro Carvalho Chehab };
2190c0d06caSMauro Carvalho Chehab 
2200c0d06caSMauro Carvalho Chehab #define MAX_EFFECTS 7
2210c0d06caSMauro Carvalho Chehab static const u8 effects_table[MAX_EFFECTS][6] = {
2220c0d06caSMauro Carvalho Chehab 	{0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00},	/* Normal */
2230c0d06caSMauro Carvalho Chehab 	{0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04},	/* Repujar */
2240c0d06caSMauro Carvalho Chehab 	{0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20},	/* Monochrome */
2250c0d06caSMauro Carvalho Chehab 	{0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80},	/* Sepia */
2260c0d06caSMauro Carvalho Chehab 	{0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02},	/* Croquis */
2270c0d06caSMauro Carvalho Chehab 	{0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10},	/* Sun Effect */
2280c0d06caSMauro Carvalho Chehab 	{0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40},	/* Negative */
2290c0d06caSMauro Carvalho Chehab };
2300c0d06caSMauro Carvalho Chehab 
2310c0d06caSMauro Carvalho Chehab #define GAMMA_MAX (15)
2320c0d06caSMauro Carvalho Chehab static const u8 gamma_table[GAMMA_MAX+1][17] = {
2330c0d06caSMauro Carvalho Chehab /* gamma table from cam1690.ini */
2340c0d06caSMauro Carvalho Chehab 	{0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21,	/* 0 */
2350c0d06caSMauro Carvalho Chehab 	 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb,
2360c0d06caSMauro Carvalho Chehab 	 0xff},
2370c0d06caSMauro Carvalho Chehab 	{0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d,	/* 1 */
2380c0d06caSMauro Carvalho Chehab 	 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1,
2390c0d06caSMauro Carvalho Chehab 	 0xff},
2400c0d06caSMauro Carvalho Chehab 	{0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35,	/* 2 */
2410c0d06caSMauro Carvalho Chehab 	 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3,
2420c0d06caSMauro Carvalho Chehab 	 0xff},
2430c0d06caSMauro Carvalho Chehab 	{0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f,	/* 3 */
2440c0d06caSMauro Carvalho Chehab 	 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6,
2450c0d06caSMauro Carvalho Chehab 	 0xff},
2460c0d06caSMauro Carvalho Chehab 	{0x00, 0x04, 0x0b, 0x15, 0x20, 0x2d, 0x3b, 0x4a,	/* 4 */
2470c0d06caSMauro Carvalho Chehab 	 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9,
2480c0d06caSMauro Carvalho Chehab 	 0xff},
2490c0d06caSMauro Carvalho Chehab 	{0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58,	/* 5 */
2500c0d06caSMauro Carvalho Chehab 	 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec,
2510c0d06caSMauro Carvalho Chehab 	 0xff},
2520c0d06caSMauro Carvalho Chehab 	{0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67,	/* 6 */
2530c0d06caSMauro Carvalho Chehab 	 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
2540c0d06caSMauro Carvalho Chehab 	 0xff},
2550c0d06caSMauro Carvalho Chehab 	{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,	/* 7 */
2560c0d06caSMauro Carvalho Chehab 	 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
2570c0d06caSMauro Carvalho Chehab 	 0xff},
2580c0d06caSMauro Carvalho Chehab 	{0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79,	/* 8 */
2590c0d06caSMauro Carvalho Chehab 	 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0,
2600c0d06caSMauro Carvalho Chehab 	 0xff},
2610c0d06caSMauro Carvalho Chehab 	{0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84,	/* 9 */
2620c0d06caSMauro Carvalho Chehab 	 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2,
2630c0d06caSMauro Carvalho Chehab 	 0xff},
2640c0d06caSMauro Carvalho Chehab 	{0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e,	/* 10 */
2650c0d06caSMauro Carvalho Chehab 	 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3,
2660c0d06caSMauro Carvalho Chehab 	 0xff},
2670c0d06caSMauro Carvalho Chehab 	{0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b,	/* 11 */
2680c0d06caSMauro Carvalho Chehab 	 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5,
2690c0d06caSMauro Carvalho Chehab 	 0xff},
2700c0d06caSMauro Carvalho Chehab 	{0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8,	/* 12 */
2710c0d06caSMauro Carvalho Chehab 	 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6,
2720c0d06caSMauro Carvalho Chehab 	 0xff},
2730c0d06caSMauro Carvalho Chehab 	{0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7,	/* 13 */
2740c0d06caSMauro Carvalho Chehab 	 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9,
2750c0d06caSMauro Carvalho Chehab 	 0xff},
2760c0d06caSMauro Carvalho Chehab 	{0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6,	/* 14 */
2770c0d06caSMauro Carvalho Chehab 	 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa,
2780c0d06caSMauro Carvalho Chehab 	 0xff},
2790c0d06caSMauro Carvalho Chehab 	{0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8,	/* 15 */
2800c0d06caSMauro Carvalho Chehab 	 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc,
2810c0d06caSMauro Carvalho Chehab 	 0xff}
2820c0d06caSMauro Carvalho Chehab };
2830c0d06caSMauro Carvalho Chehab 
2840c0d06caSMauro Carvalho Chehab static const u8 tas5130a_sensor_init[][8] = {
2850c0d06caSMauro Carvalho Chehab 	{0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09},
2860c0d06caSMauro Carvalho Chehab 	{0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09},
2870c0d06caSMauro Carvalho Chehab 	{0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09},
2880c0d06caSMauro Carvalho Chehab };
2890c0d06caSMauro Carvalho Chehab 
2900c0d06caSMauro Carvalho Chehab static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07};
2910c0d06caSMauro Carvalho Chehab 
2920c0d06caSMauro Carvalho Chehab /* read 1 byte */
reg_r(struct gspca_dev * gspca_dev,u16 index)2930c0d06caSMauro Carvalho Chehab static u8 reg_r(struct gspca_dev *gspca_dev,
2940c0d06caSMauro Carvalho Chehab 		   u16 index)
2950c0d06caSMauro Carvalho Chehab {
2960c0d06caSMauro Carvalho Chehab 	usb_control_msg(gspca_dev->dev,
2970c0d06caSMauro Carvalho Chehab 			usb_rcvctrlpipe(gspca_dev->dev, 0),
2980c0d06caSMauro Carvalho Chehab 			0,		/* request */
2990c0d06caSMauro Carvalho Chehab 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3000c0d06caSMauro Carvalho Chehab 			0,		/* value */
3010c0d06caSMauro Carvalho Chehab 			index,
3020c0d06caSMauro Carvalho Chehab 			gspca_dev->usb_buf, 1, 500);
3030c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_buf[0];
3040c0d06caSMauro Carvalho Chehab }
3050c0d06caSMauro Carvalho Chehab 
reg_w(struct gspca_dev * gspca_dev,u16 index)3060c0d06caSMauro Carvalho Chehab static void reg_w(struct gspca_dev *gspca_dev,
3070c0d06caSMauro Carvalho Chehab 		  u16 index)
3080c0d06caSMauro Carvalho Chehab {
3090c0d06caSMauro Carvalho Chehab 	usb_control_msg(gspca_dev->dev,
3100c0d06caSMauro Carvalho Chehab 			usb_sndctrlpipe(gspca_dev->dev, 0),
3110c0d06caSMauro Carvalho Chehab 			0,
3120c0d06caSMauro Carvalho Chehab 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3130c0d06caSMauro Carvalho Chehab 			0, index,
3140c0d06caSMauro Carvalho Chehab 			NULL, 0, 500);
3150c0d06caSMauro Carvalho Chehab }
3160c0d06caSMauro Carvalho Chehab 
reg_w_buf(struct gspca_dev * gspca_dev,const u8 * buffer,u16 len)3170c0d06caSMauro Carvalho Chehab static void reg_w_buf(struct gspca_dev *gspca_dev,
3180c0d06caSMauro Carvalho Chehab 		  const u8 *buffer, u16 len)
3190c0d06caSMauro Carvalho Chehab {
3200c0d06caSMauro Carvalho Chehab 	if (len <= USB_BUF_SZ) {
3210c0d06caSMauro Carvalho Chehab 		memcpy(gspca_dev->usb_buf, buffer, len);
3220c0d06caSMauro Carvalho Chehab 		usb_control_msg(gspca_dev->dev,
3230c0d06caSMauro Carvalho Chehab 				usb_sndctrlpipe(gspca_dev->dev, 0),
3240c0d06caSMauro Carvalho Chehab 				0,
3250c0d06caSMauro Carvalho Chehab 			   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3260c0d06caSMauro Carvalho Chehab 				0x01, 0,
3270c0d06caSMauro Carvalho Chehab 				gspca_dev->usb_buf, len, 500);
3280c0d06caSMauro Carvalho Chehab 	} else {
3290c0d06caSMauro Carvalho Chehab 		u8 *tmpbuf;
3300c0d06caSMauro Carvalho Chehab 
3310c0d06caSMauro Carvalho Chehab 		tmpbuf = kmemdup(buffer, len, GFP_KERNEL);
3320c0d06caSMauro Carvalho Chehab 		if (!tmpbuf) {
3330c0d06caSMauro Carvalho Chehab 			pr_err("Out of memory\n");
3340c0d06caSMauro Carvalho Chehab 			return;
3350c0d06caSMauro Carvalho Chehab 		}
3360c0d06caSMauro Carvalho Chehab 		usb_control_msg(gspca_dev->dev,
3370c0d06caSMauro Carvalho Chehab 				usb_sndctrlpipe(gspca_dev->dev, 0),
3380c0d06caSMauro Carvalho Chehab 				0,
3390c0d06caSMauro Carvalho Chehab 			   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3400c0d06caSMauro Carvalho Chehab 				0x01, 0,
3410c0d06caSMauro Carvalho Chehab 				tmpbuf, len, 500);
3420c0d06caSMauro Carvalho Chehab 		kfree(tmpbuf);
3430c0d06caSMauro Carvalho Chehab 	}
3440c0d06caSMauro Carvalho Chehab }
3450c0d06caSMauro Carvalho Chehab 
3460c0d06caSMauro Carvalho Chehab /* write values to consecutive registers */
reg_w_ixbuf(struct gspca_dev * gspca_dev,u8 reg,const u8 * buffer,u16 len)3470c0d06caSMauro Carvalho Chehab static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
3480c0d06caSMauro Carvalho Chehab 			u8 reg,
3490c0d06caSMauro Carvalho Chehab 			const u8 *buffer, u16 len)
3500c0d06caSMauro Carvalho Chehab {
3510c0d06caSMauro Carvalho Chehab 	int i;
3520c0d06caSMauro Carvalho Chehab 	u8 *p, *tmpbuf;
3530c0d06caSMauro Carvalho Chehab 
3540c0d06caSMauro Carvalho Chehab 	if (len * 2 <= USB_BUF_SZ) {
3550c0d06caSMauro Carvalho Chehab 		p = tmpbuf = gspca_dev->usb_buf;
3560c0d06caSMauro Carvalho Chehab 	} else {
3576da2ec56SKees Cook 		p = tmpbuf = kmalloc_array(len, 2, GFP_KERNEL);
3580c0d06caSMauro Carvalho Chehab 		if (!tmpbuf) {
3590c0d06caSMauro Carvalho Chehab 			pr_err("Out of memory\n");
3600c0d06caSMauro Carvalho Chehab 			return;
3610c0d06caSMauro Carvalho Chehab 		}
3620c0d06caSMauro Carvalho Chehab 	}
3630c0d06caSMauro Carvalho Chehab 	i = len;
3640c0d06caSMauro Carvalho Chehab 	while (--i >= 0) {
3650c0d06caSMauro Carvalho Chehab 		*p++ = reg++;
3660c0d06caSMauro Carvalho Chehab 		*p++ = *buffer++;
3670c0d06caSMauro Carvalho Chehab 	}
3680c0d06caSMauro Carvalho Chehab 	usb_control_msg(gspca_dev->dev,
3690c0d06caSMauro Carvalho Chehab 			usb_sndctrlpipe(gspca_dev->dev, 0),
3700c0d06caSMauro Carvalho Chehab 			0,
3710c0d06caSMauro Carvalho Chehab 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3720c0d06caSMauro Carvalho Chehab 			0x01, 0,
3730c0d06caSMauro Carvalho Chehab 			tmpbuf, len * 2, 500);
3740c0d06caSMauro Carvalho Chehab 	if (len * 2 > USB_BUF_SZ)
3750c0d06caSMauro Carvalho Chehab 		kfree(tmpbuf);
3760c0d06caSMauro Carvalho Chehab }
3770c0d06caSMauro Carvalho Chehab 
om6802_sensor_init(struct gspca_dev * gspca_dev)3780c0d06caSMauro Carvalho Chehab static void om6802_sensor_init(struct gspca_dev *gspca_dev)
3790c0d06caSMauro Carvalho Chehab {
3800c0d06caSMauro Carvalho Chehab 	int i;
3810c0d06caSMauro Carvalho Chehab 	const u8 *p;
3820c0d06caSMauro Carvalho Chehab 	u8 byte;
3830c0d06caSMauro Carvalho Chehab 	u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05};
3840c0d06caSMauro Carvalho Chehab 	static const u8 sensor_init[] = {
3850c0d06caSMauro Carvalho Chehab 		0xdf, 0x6d,
3860c0d06caSMauro Carvalho Chehab 		0xdd, 0x18,
3870c0d06caSMauro Carvalho Chehab 		0x5a, 0xe0,
3880c0d06caSMauro Carvalho Chehab 		0x5c, 0x07,
3890c0d06caSMauro Carvalho Chehab 		0x5d, 0xb0,
3900c0d06caSMauro Carvalho Chehab 		0x5e, 0x1e,
3910c0d06caSMauro Carvalho Chehab 		0x60, 0x71,
3920c0d06caSMauro Carvalho Chehab 		0xef, 0x00,
3930c0d06caSMauro Carvalho Chehab 		0xe9, 0x00,
3940c0d06caSMauro Carvalho Chehab 		0xea, 0x00,
3950c0d06caSMauro Carvalho Chehab 		0x90, 0x24,
3960c0d06caSMauro Carvalho Chehab 		0x91, 0xb2,
3970c0d06caSMauro Carvalho Chehab 		0x82, 0x32,
3980c0d06caSMauro Carvalho Chehab 		0xfd, 0x41,
3990c0d06caSMauro Carvalho Chehab 		0x00			/* table end */
4000c0d06caSMauro Carvalho Chehab 	};
4010c0d06caSMauro Carvalho Chehab 
4020c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
4030c0d06caSMauro Carvalho Chehab 	msleep(100);
4040c0d06caSMauro Carvalho Chehab 	i = 4;
4050c0d06caSMauro Carvalho Chehab 	while (--i > 0) {
4060c0d06caSMauro Carvalho Chehab 		byte = reg_r(gspca_dev, 0x0060);
4070c0d06caSMauro Carvalho Chehab 		if (!(byte & 0x01))
4080c0d06caSMauro Carvalho Chehab 			break;
4090c0d06caSMauro Carvalho Chehab 		msleep(100);
4100c0d06caSMauro Carvalho Chehab 	}
4110c0d06caSMauro Carvalho Chehab 	byte = reg_r(gspca_dev, 0x0063);
4120c0d06caSMauro Carvalho Chehab 	if (byte != 0x17) {
4130c0d06caSMauro Carvalho Chehab 		pr_err("Bad sensor reset %02x\n", byte);
4140c0d06caSMauro Carvalho Chehab 		/* continue? */
4150c0d06caSMauro Carvalho Chehab 	}
4160c0d06caSMauro Carvalho Chehab 
4170c0d06caSMauro Carvalho Chehab 	p = sensor_init;
4180c0d06caSMauro Carvalho Chehab 	while (*p != 0) {
4190c0d06caSMauro Carvalho Chehab 		val[1] = *p++;
4200c0d06caSMauro Carvalho Chehab 		val[3] = *p++;
4210c0d06caSMauro Carvalho Chehab 		if (*p == 0)
4220c0d06caSMauro Carvalho Chehab 			reg_w(gspca_dev, 0x3c80);
4230c0d06caSMauro Carvalho Chehab 		reg_w_buf(gspca_dev, val, sizeof val);
4240c0d06caSMauro Carvalho Chehab 		i = 4;
4250c0d06caSMauro Carvalho Chehab 		while (--i >= 0) {
4260c0d06caSMauro Carvalho Chehab 			msleep(15);
4270c0d06caSMauro Carvalho Chehab 			byte = reg_r(gspca_dev, 0x60);
4280c0d06caSMauro Carvalho Chehab 			if (!(byte & 0x01))
4290c0d06caSMauro Carvalho Chehab 				break;
4300c0d06caSMauro Carvalho Chehab 		}
4310c0d06caSMauro Carvalho Chehab 	}
4320c0d06caSMauro Carvalho Chehab 	msleep(15);
4330c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, 0x3c80);
4340c0d06caSMauro Carvalho Chehab }
4350c0d06caSMauro Carvalho Chehab 
4360c0d06caSMauro Carvalho Chehab /* this function is called at probe time */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)4370c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev,
4380c0d06caSMauro Carvalho Chehab 		     const struct usb_device_id *id)
4390c0d06caSMauro Carvalho Chehab {
4400c0d06caSMauro Carvalho Chehab 	struct cam *cam  = &gspca_dev->cam;
4410c0d06caSMauro Carvalho Chehab 
4420c0d06caSMauro Carvalho Chehab 	cam->cam_mode = vga_mode_t16;
4430c0d06caSMauro Carvalho Chehab 	cam->nmodes = ARRAY_SIZE(vga_mode_t16);
4440c0d06caSMauro Carvalho Chehab 
4450c0d06caSMauro Carvalho Chehab 	return 0;
4460c0d06caSMauro Carvalho Chehab }
4470c0d06caSMauro Carvalho Chehab 
setbrightness(struct gspca_dev * gspca_dev,s32 brightness)4480c0d06caSMauro Carvalho Chehab static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
4490c0d06caSMauro Carvalho Chehab {
4500c0d06caSMauro Carvalho Chehab 	u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
4510c0d06caSMauro Carvalho Chehab 
4520c0d06caSMauro Carvalho Chehab 	if (brightness < 7) {
4530c0d06caSMauro Carvalho Chehab 		set6[1] = 0x26;
4540c0d06caSMauro Carvalho Chehab 		set6[3] = 0x70 - brightness * 0x10;
4550c0d06caSMauro Carvalho Chehab 	} else {
4560c0d06caSMauro Carvalho Chehab 		set6[3] = 0x00 + ((brightness - 7) * 0x10);
4570c0d06caSMauro Carvalho Chehab 	}
4580c0d06caSMauro Carvalho Chehab 
4590c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, set6, sizeof set6);
4600c0d06caSMauro Carvalho Chehab }
4610c0d06caSMauro Carvalho Chehab 
setcontrast(struct gspca_dev * gspca_dev,s32 contrast)4620c0d06caSMauro Carvalho Chehab static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast)
4630c0d06caSMauro Carvalho Chehab {
4640c0d06caSMauro Carvalho Chehab 	u16 reg_to_write;
4650c0d06caSMauro Carvalho Chehab 
4660c0d06caSMauro Carvalho Chehab 	if (contrast < 7)
4670c0d06caSMauro Carvalho Chehab 		reg_to_write = 0x8ea9 - contrast * 0x200;
4680c0d06caSMauro Carvalho Chehab 	else
4690c0d06caSMauro Carvalho Chehab 		reg_to_write = 0x00a9 + (contrast - 7) * 0x200;
4700c0d06caSMauro Carvalho Chehab 
4710c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, reg_to_write);
4720c0d06caSMauro Carvalho Chehab }
4730c0d06caSMauro Carvalho Chehab 
setcolors(struct gspca_dev * gspca_dev,s32 val)4740c0d06caSMauro Carvalho Chehab static void setcolors(struct gspca_dev *gspca_dev, s32 val)
4750c0d06caSMauro Carvalho Chehab {
4760c0d06caSMauro Carvalho Chehab 	u16 reg_to_write;
4770c0d06caSMauro Carvalho Chehab 
4780c0d06caSMauro Carvalho Chehab 	reg_to_write = 0x80bb + val * 0x100;	/* was 0xc0 */
4790c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, reg_to_write);
4800c0d06caSMauro Carvalho Chehab }
4810c0d06caSMauro Carvalho Chehab 
setgamma(struct gspca_dev * gspca_dev,s32 val)4820c0d06caSMauro Carvalho Chehab static void setgamma(struct gspca_dev *gspca_dev, s32 val)
4830c0d06caSMauro Carvalho Chehab {
48437d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_CONF, "Gamma: %d\n", val);
4850c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0x90,
4860c0d06caSMauro Carvalho Chehab 		gamma_table[val], sizeof gamma_table[0]);
4870c0d06caSMauro Carvalho Chehab }
4880c0d06caSMauro Carvalho Chehab 
setawb_n_RGB(struct gspca_dev * gspca_dev)4890c0d06caSMauro Carvalho Chehab static void setawb_n_RGB(struct gspca_dev *gspca_dev)
4900c0d06caSMauro Carvalho Chehab {
4910c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4920c0d06caSMauro Carvalho Chehab 	u8 all_gain_reg[8] = {
4930c0d06caSMauro Carvalho Chehab 		0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 };
4940c0d06caSMauro Carvalho Chehab 	s32 red_gain, blue_gain, green_gain;
4950c0d06caSMauro Carvalho Chehab 
4960c0d06caSMauro Carvalho Chehab 	green_gain = sd->gain->val;
4970c0d06caSMauro Carvalho Chehab 
4980c0d06caSMauro Carvalho Chehab 	red_gain = green_gain + sd->red_balance->val;
4990c0d06caSMauro Carvalho Chehab 	if (red_gain > 0x40)
5000c0d06caSMauro Carvalho Chehab 		red_gain = 0x40;
5010c0d06caSMauro Carvalho Chehab 	else if (red_gain < 0x10)
5020c0d06caSMauro Carvalho Chehab 		red_gain = 0x10;
5030c0d06caSMauro Carvalho Chehab 
5040c0d06caSMauro Carvalho Chehab 	blue_gain = green_gain + sd->blue_balance->val;
5050c0d06caSMauro Carvalho Chehab 	if (blue_gain > 0x40)
5060c0d06caSMauro Carvalho Chehab 		blue_gain = 0x40;
5070c0d06caSMauro Carvalho Chehab 	else if (blue_gain < 0x10)
5080c0d06caSMauro Carvalho Chehab 		blue_gain = 0x10;
5090c0d06caSMauro Carvalho Chehab 
5100c0d06caSMauro Carvalho Chehab 	all_gain_reg[1] = red_gain;
5110c0d06caSMauro Carvalho Chehab 	all_gain_reg[3] = blue_gain;
5120c0d06caSMauro Carvalho Chehab 	all_gain_reg[5] = green_gain;
5130c0d06caSMauro Carvalho Chehab 	all_gain_reg[7] = sensor_data[sd->sensor].reg80;
5140c0d06caSMauro Carvalho Chehab 	if (!sd->awb->val)
5150c0d06caSMauro Carvalho Chehab 		all_gain_reg[7] &= ~0x04; /* AWB off */
5160c0d06caSMauro Carvalho Chehab 
5170c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
5180c0d06caSMauro Carvalho Chehab }
5190c0d06caSMauro Carvalho Chehab 
setsharpness(struct gspca_dev * gspca_dev,s32 val)5200c0d06caSMauro Carvalho Chehab static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
5210c0d06caSMauro Carvalho Chehab {
5220c0d06caSMauro Carvalho Chehab 	u16 reg_to_write;
5230c0d06caSMauro Carvalho Chehab 
5240c0d06caSMauro Carvalho Chehab 	reg_to_write = 0x0aa6 + 0x1000 * val;
5250c0d06caSMauro Carvalho Chehab 
5260c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, reg_to_write);
5270c0d06caSMauro Carvalho Chehab }
5280c0d06caSMauro Carvalho Chehab 
setfreq(struct gspca_dev * gspca_dev,s32 val)5290c0d06caSMauro Carvalho Chehab static void setfreq(struct gspca_dev *gspca_dev, s32 val)
5300c0d06caSMauro Carvalho Chehab {
5310c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5320c0d06caSMauro Carvalho Chehab 	u8 reg66;
5330c0d06caSMauro Carvalho Chehab 	u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 };
5340c0d06caSMauro Carvalho Chehab 
5350c0d06caSMauro Carvalho Chehab 	switch (sd->sensor) {
5360c0d06caSMauro Carvalho Chehab 	case SENSOR_LT168G:
5370c0d06caSMauro Carvalho Chehab 		if (val != 0)
5380c0d06caSMauro Carvalho Chehab 			freq[3] = 0xa8;
5390c0d06caSMauro Carvalho Chehab 		reg66 = 0x41;
5400c0d06caSMauro Carvalho Chehab 		break;
5410c0d06caSMauro Carvalho Chehab 	case SENSOR_OM6802:
5420c0d06caSMauro Carvalho Chehab 		reg66 = 0xca;
5430c0d06caSMauro Carvalho Chehab 		break;
5440c0d06caSMauro Carvalho Chehab 	default:
5450c0d06caSMauro Carvalho Chehab 		reg66 = 0x40;
5460c0d06caSMauro Carvalho Chehab 		break;
5470c0d06caSMauro Carvalho Chehab 	}
5480c0d06caSMauro Carvalho Chehab 	switch (val) {
5490c0d06caSMauro Carvalho Chehab 	case 0:				/* no flicker */
5500c0d06caSMauro Carvalho Chehab 		freq[3] = 0xf0;
5510c0d06caSMauro Carvalho Chehab 		break;
5520c0d06caSMauro Carvalho Chehab 	case 2:				/* 60Hz */
5530c0d06caSMauro Carvalho Chehab 		reg66 &= ~0x40;
5540c0d06caSMauro Carvalho Chehab 		break;
5550c0d06caSMauro Carvalho Chehab 	}
5560c0d06caSMauro Carvalho Chehab 	freq[1] = reg66;
5570c0d06caSMauro Carvalho Chehab 
5580c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, freq, sizeof freq);
5590c0d06caSMauro Carvalho Chehab }
5600c0d06caSMauro Carvalho Chehab 
5610c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)5620c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev)
5630c0d06caSMauro Carvalho Chehab {
564176180c2SMasahiro Yamada 	/* some of this registers are not really needed, because
565176180c2SMasahiro Yamada 	 * they are overridden by setbrigthness, setcontrast, etc.,
566176180c2SMasahiro Yamada 	 * but won't hurt anyway, and can help someone with similar webcam
5670c0d06caSMauro Carvalho Chehab 	 * to see the initial parameters.*/
5680c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5690c0d06caSMauro Carvalho Chehab 	const struct additional_sensor_data *sensor;
5700c0d06caSMauro Carvalho Chehab 	int i;
5710c0d06caSMauro Carvalho Chehab 	u16 sensor_id;
5720c0d06caSMauro Carvalho Chehab 	u8 test_byte = 0;
5730c0d06caSMauro Carvalho Chehab 
5740c0d06caSMauro Carvalho Chehab 	static const u8 read_indexs[] =
5750c0d06caSMauro Carvalho Chehab 		{ 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
5760c0d06caSMauro Carvalho Chehab 		  0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 };
5770c0d06caSMauro Carvalho Chehab 	static const u8 n1[] =
5780c0d06caSMauro Carvalho Chehab 			{0x08, 0x03, 0x09, 0x03, 0x12, 0x04};
5790c0d06caSMauro Carvalho Chehab 	static const u8 n2[] =
5800c0d06caSMauro Carvalho Chehab 			{0x08, 0x00};
5810c0d06caSMauro Carvalho Chehab 
5820c0d06caSMauro Carvalho Chehab 	sensor_id = (reg_r(gspca_dev, 0x06) << 8)
5830c0d06caSMauro Carvalho Chehab 			| reg_r(gspca_dev, 0x07);
5840c0d06caSMauro Carvalho Chehab 	switch (sensor_id & 0xff0f) {
5850c0d06caSMauro Carvalho Chehab 	case 0x0801:
58637d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PROBE, "sensor tas5130a\n");
5870c0d06caSMauro Carvalho Chehab 		sd->sensor = SENSOR_TAS5130A;
5880c0d06caSMauro Carvalho Chehab 		break;
5890c0d06caSMauro Carvalho Chehab 	case 0x0802:
59037d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PROBE, "sensor lt168g\n");
5910c0d06caSMauro Carvalho Chehab 		sd->sensor = SENSOR_LT168G;
5920c0d06caSMauro Carvalho Chehab 		break;
5930c0d06caSMauro Carvalho Chehab 	case 0x0803:
59437d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PROBE, "sensor 'other'\n");
5950c0d06caSMauro Carvalho Chehab 		sd->sensor = SENSOR_OTHER;
5960c0d06caSMauro Carvalho Chehab 		break;
5970c0d06caSMauro Carvalho Chehab 	case 0x0807:
59837d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PROBE, "sensor om6802\n");
5990c0d06caSMauro Carvalho Chehab 		sd->sensor = SENSOR_OM6802;
6000c0d06caSMauro Carvalho Chehab 		break;
6010c0d06caSMauro Carvalho Chehab 	default:
6020c0d06caSMauro Carvalho Chehab 		pr_err("unknown sensor %04x\n", sensor_id);
6030c0d06caSMauro Carvalho Chehab 		return -EINVAL;
6040c0d06caSMauro Carvalho Chehab 	}
6050c0d06caSMauro Carvalho Chehab 
6060c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_OM6802) {
6070c0d06caSMauro Carvalho Chehab 		reg_w_buf(gspca_dev, n1, sizeof n1);
6080c0d06caSMauro Carvalho Chehab 		i = 5;
6090c0d06caSMauro Carvalho Chehab 		while (--i >= 0) {
6100c0d06caSMauro Carvalho Chehab 			reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
6110c0d06caSMauro Carvalho Chehab 			test_byte = reg_r(gspca_dev, 0x0063);
6120c0d06caSMauro Carvalho Chehab 			msleep(100);
6130c0d06caSMauro Carvalho Chehab 			if (test_byte == 0x17)
6140c0d06caSMauro Carvalho Chehab 				break;		/* OK */
6150c0d06caSMauro Carvalho Chehab 		}
6160c0d06caSMauro Carvalho Chehab 		if (i < 0) {
6170c0d06caSMauro Carvalho Chehab 			pr_err("Bad sensor reset %02x\n", test_byte);
6180c0d06caSMauro Carvalho Chehab 			return -EIO;
6190c0d06caSMauro Carvalho Chehab 		}
6200c0d06caSMauro Carvalho Chehab 		reg_w_buf(gspca_dev, n2, sizeof n2);
6210c0d06caSMauro Carvalho Chehab 	}
6220c0d06caSMauro Carvalho Chehab 
6230c0d06caSMauro Carvalho Chehab 	i = 0;
6240c0d06caSMauro Carvalho Chehab 	while (read_indexs[i] != 0x00) {
6250c0d06caSMauro Carvalho Chehab 		test_byte = reg_r(gspca_dev, read_indexs[i]);
62637d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Reg 0x%02x = 0x%02x\n",
62737d5efb0SJoe Perches 			  read_indexs[i], test_byte);
6280c0d06caSMauro Carvalho Chehab 		i++;
6290c0d06caSMauro Carvalho Chehab 	}
6300c0d06caSMauro Carvalho Chehab 
6310c0d06caSMauro Carvalho Chehab 	sensor = &sensor_data[sd->sensor];
6320c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3);
6330c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz);
6340c0d06caSMauro Carvalho Chehab 
6350c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_LT168G) {
6360c0d06caSMauro Carvalho Chehab 		test_byte = reg_r(gspca_dev, 0x80);
63737d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Reg 0x%02x = 0x%02x\n", 0x80,
6380c0d06caSMauro Carvalho Chehab 			  test_byte);
6390c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x6c80);
6400c0d06caSMauro Carvalho Chehab 	}
6410c0d06caSMauro Carvalho Chehab 
6420c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
6430c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
6440c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
6450c0d06caSMauro Carvalho Chehab 
6460c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
6470c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
6480c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e);
6490c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, (0x20 << 8) + 0x87);
6500c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, (0x20 << 8) + 0x88);
6510c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, (0x20 << 8) + 0x89);
6520c0d06caSMauro Carvalho Chehab 
6530c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5);
6540c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8);
6550c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
6560c0d06caSMauro Carvalho Chehab 
6570c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_LT168G) {
6580c0d06caSMauro Carvalho Chehab 		test_byte = reg_r(gspca_dev, 0x80);
65937d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Reg 0x%02x = 0x%02x\n", 0x80,
6600c0d06caSMauro Carvalho Chehab 			  test_byte);
6610c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x6c80);
6620c0d06caSMauro Carvalho Chehab 	}
6630c0d06caSMauro Carvalho Chehab 
6640c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
6650c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
6660c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
6670c0d06caSMauro Carvalho Chehab 
6680c0d06caSMauro Carvalho Chehab 	return 0;
6690c0d06caSMauro Carvalho Chehab }
6700c0d06caSMauro Carvalho Chehab 
setmirror(struct gspca_dev * gspca_dev,s32 val)6710c0d06caSMauro Carvalho Chehab static void setmirror(struct gspca_dev *gspca_dev, s32 val)
6720c0d06caSMauro Carvalho Chehab {
6730c0d06caSMauro Carvalho Chehab 	u8 hflipcmd[8] =
6740c0d06caSMauro Carvalho Chehab 		{0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
6750c0d06caSMauro Carvalho Chehab 
6760c0d06caSMauro Carvalho Chehab 	if (val)
6770c0d06caSMauro Carvalho Chehab 		hflipcmd[3] = 0x01;
6780c0d06caSMauro Carvalho Chehab 
6790c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd);
6800c0d06caSMauro Carvalho Chehab }
6810c0d06caSMauro Carvalho Chehab 
seteffect(struct gspca_dev * gspca_dev,s32 val)6820c0d06caSMauro Carvalho Chehab static void seteffect(struct gspca_dev *gspca_dev, s32 val)
6830c0d06caSMauro Carvalho Chehab {
6840c0d06caSMauro Carvalho Chehab 	int idx = 0;
6850c0d06caSMauro Carvalho Chehab 
6860c0d06caSMauro Carvalho Chehab 	switch (val) {
6870c0d06caSMauro Carvalho Chehab 	case V4L2_COLORFX_NONE:
6880c0d06caSMauro Carvalho Chehab 		break;
6890c0d06caSMauro Carvalho Chehab 	case V4L2_COLORFX_BW:
6900c0d06caSMauro Carvalho Chehab 		idx = 2;
6910c0d06caSMauro Carvalho Chehab 		break;
6920c0d06caSMauro Carvalho Chehab 	case V4L2_COLORFX_SEPIA:
6930c0d06caSMauro Carvalho Chehab 		idx = 3;
6940c0d06caSMauro Carvalho Chehab 		break;
6950c0d06caSMauro Carvalho Chehab 	case V4L2_COLORFX_SKETCH:
6960c0d06caSMauro Carvalho Chehab 		idx = 4;
6970c0d06caSMauro Carvalho Chehab 		break;
6980c0d06caSMauro Carvalho Chehab 	case V4L2_COLORFX_NEGATIVE:
6990c0d06caSMauro Carvalho Chehab 		idx = 6;
7000c0d06caSMauro Carvalho Chehab 		break;
7010c0d06caSMauro Carvalho Chehab 	default:
7020c0d06caSMauro Carvalho Chehab 		break;
7030c0d06caSMauro Carvalho Chehab 	}
7040c0d06caSMauro Carvalho Chehab 
7050c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, effects_table[idx],
7060c0d06caSMauro Carvalho Chehab 				sizeof effects_table[0]);
7070c0d06caSMauro Carvalho Chehab 
7080c0d06caSMauro Carvalho Chehab 	if (val == V4L2_COLORFX_SKETCH)
7090c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x4aa6);
7100c0d06caSMauro Carvalho Chehab 	else
7110c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0xfaa6);
7120c0d06caSMauro Carvalho Chehab }
7130c0d06caSMauro Carvalho Chehab 
7140c0d06caSMauro Carvalho Chehab /* Is this really needed?
7150c0d06caSMauro Carvalho Chehab  * i added some module parameters for test with some users */
poll_sensor(struct gspca_dev * gspca_dev)7160c0d06caSMauro Carvalho Chehab static void poll_sensor(struct gspca_dev *gspca_dev)
7170c0d06caSMauro Carvalho Chehab {
7180c0d06caSMauro Carvalho Chehab 	static const u8 poll1[] =
7190c0d06caSMauro Carvalho Chehab 		{0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82,
7200c0d06caSMauro Carvalho Chehab 		 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34,
7210c0d06caSMauro Carvalho Chehab 		 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01,
7220c0d06caSMauro Carvalho Chehab 		 0x60, 0x14};
7230c0d06caSMauro Carvalho Chehab 	static const u8 poll2[] =
7240c0d06caSMauro Carvalho Chehab 		{0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9,
7250c0d06caSMauro Carvalho Chehab 		 0x73, 0x02, 0x73, 0x02, 0x60, 0x14};
7260c0d06caSMauro Carvalho Chehab 	static const u8 noise03[] =	/* (some differences / ms-drv) */
7270c0d06caSMauro Carvalho Chehab 		{0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f,
7280c0d06caSMauro Carvalho Chehab 		 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c,
7290c0d06caSMauro Carvalho Chehab 		 0xc2, 0x80, 0xc3, 0x10};
7300c0d06caSMauro Carvalho Chehab 
73137d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "[Sensor requires polling]\n");
7320c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, poll1, sizeof poll1);
7330c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, poll2, sizeof poll2);
7340c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, noise03, sizeof noise03);
7350c0d06caSMauro Carvalho Chehab }
7360c0d06caSMauro Carvalho Chehab 
sd_start(struct gspca_dev * gspca_dev)7370c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev)
7380c0d06caSMauro Carvalho Chehab {
7390c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
7400c0d06caSMauro Carvalho Chehab 	const struct additional_sensor_data *sensor;
7410c0d06caSMauro Carvalho Chehab 	int i, mode;
7420c0d06caSMauro Carvalho Chehab 	u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
7430c0d06caSMauro Carvalho Chehab 	static const u8 t3[] =
7440c0d06caSMauro Carvalho Chehab 		{ 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 };
7450c0d06caSMauro Carvalho Chehab 
7460c0d06caSMauro Carvalho Chehab 	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
7470c0d06caSMauro Carvalho Chehab 	switch (mode) {
7480c0d06caSMauro Carvalho Chehab 	case 0:		/* 640x480 (0x00) */
7490c0d06caSMauro Carvalho Chehab 		break;
7500c0d06caSMauro Carvalho Chehab 	case 1:		/* 352x288 */
7510c0d06caSMauro Carvalho Chehab 		t2[1] = 0x40;
7520c0d06caSMauro Carvalho Chehab 		break;
7530c0d06caSMauro Carvalho Chehab 	case 2:		/* 320x240 */
7540c0d06caSMauro Carvalho Chehab 		t2[1] = 0x10;
7550c0d06caSMauro Carvalho Chehab 		break;
7560c0d06caSMauro Carvalho Chehab 	case 3:		/* 176x144 */
7570c0d06caSMauro Carvalho Chehab 		t2[1] = 0x50;
7580c0d06caSMauro Carvalho Chehab 		break;
7590c0d06caSMauro Carvalho Chehab 	default:
7600c0d06caSMauro Carvalho Chehab /*	case 4:		 * 160x120 */
7610c0d06caSMauro Carvalho Chehab 		t2[1] = 0x20;
7620c0d06caSMauro Carvalho Chehab 		break;
7630c0d06caSMauro Carvalho Chehab 	}
7640c0d06caSMauro Carvalho Chehab 
7650c0d06caSMauro Carvalho Chehab 	switch (sd->sensor) {
7660c0d06caSMauro Carvalho Chehab 	case SENSOR_OM6802:
7670c0d06caSMauro Carvalho Chehab 		om6802_sensor_init(gspca_dev);
7680c0d06caSMauro Carvalho Chehab 		break;
7690c0d06caSMauro Carvalho Chehab 	case SENSOR_TAS5130A:
7700c0d06caSMauro Carvalho Chehab 		i = 0;
7710c0d06caSMauro Carvalho Chehab 		for (;;) {
7720c0d06caSMauro Carvalho Chehab 			reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
7730c0d06caSMauro Carvalho Chehab 					 sizeof tas5130a_sensor_init[0]);
7740c0d06caSMauro Carvalho Chehab 			if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1)
7750c0d06caSMauro Carvalho Chehab 				break;
7760c0d06caSMauro Carvalho Chehab 			i++;
7770c0d06caSMauro Carvalho Chehab 		}
7780c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x3c80);
7790c0d06caSMauro Carvalho Chehab 		/* just in case and to keep sync with logs (for mine) */
7800c0d06caSMauro Carvalho Chehab 		reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
7810c0d06caSMauro Carvalho Chehab 				 sizeof tas5130a_sensor_init[0]);
7820c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x3c80);
7830c0d06caSMauro Carvalho Chehab 		break;
7840c0d06caSMauro Carvalho Chehab 	}
7850c0d06caSMauro Carvalho Chehab 	sensor = &sensor_data[sd->sensor];
7860c0d06caSMauro Carvalho Chehab 	setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
7870c0d06caSMauro Carvalho Chehab 	reg_r(gspca_dev, 0x0012);
7880c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, t2, sizeof t2);
7890c0d06caSMauro Carvalho Chehab 	reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3);
7900c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, 0x0013);
7910c0d06caSMauro Carvalho Chehab 	msleep(15);
7920c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
7930c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
7940c0d06caSMauro Carvalho Chehab 
7950c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_OM6802)
7960c0d06caSMauro Carvalho Chehab 		poll_sensor(gspca_dev);
7970c0d06caSMauro Carvalho Chehab 
7980c0d06caSMauro Carvalho Chehab 	return 0;
7990c0d06caSMauro Carvalho Chehab }
8000c0d06caSMauro Carvalho Chehab 
sd_stopN(struct gspca_dev * gspca_dev)8010c0d06caSMauro Carvalho Chehab static void sd_stopN(struct gspca_dev *gspca_dev)
8020c0d06caSMauro Carvalho Chehab {
8030c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
8040c0d06caSMauro Carvalho Chehab 
8050c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
8060c0d06caSMauro Carvalho Chehab 			sizeof sensor_data[sd->sensor].stream);
8070c0d06caSMauro Carvalho Chehab 	reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
8080c0d06caSMauro Carvalho Chehab 			sizeof sensor_data[sd->sensor].stream);
8090c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_OM6802) {
8100c0d06caSMauro Carvalho Chehab 		msleep(20);
8110c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x0309);
8120c0d06caSMauro Carvalho Chehab 	}
813f8d26879SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT)
8140c0d06caSMauro Carvalho Chehab 	/* If the last button state is pressed, release it now! */
8150c0d06caSMauro Carvalho Chehab 	if (sd->button_pressed) {
8160c0d06caSMauro Carvalho Chehab 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
8170c0d06caSMauro Carvalho Chehab 		input_sync(gspca_dev->input_dev);
8180c0d06caSMauro Carvalho Chehab 		sd->button_pressed = 0;
8190c0d06caSMauro Carvalho Chehab 	}
8200c0d06caSMauro Carvalho Chehab #endif
8210c0d06caSMauro Carvalho Chehab }
8220c0d06caSMauro Carvalho Chehab 
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)8230c0d06caSMauro Carvalho Chehab static void sd_pkt_scan(struct gspca_dev *gspca_dev,
8240c0d06caSMauro Carvalho Chehab 			u8 *data,			/* isoc packet */
8250c0d06caSMauro Carvalho Chehab 			int len)			/* iso packet length */
8260c0d06caSMauro Carvalho Chehab {
827d7e92e15SArnd Bergmann 	struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
8280c0d06caSMauro Carvalho Chehab 	int pkt_type;
8290c0d06caSMauro Carvalho Chehab 
8300c0d06caSMauro Carvalho Chehab 	if (data[0] == 0x5a) {
831f8d26879SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT)
8320c0d06caSMauro Carvalho Chehab 		if (len > 20) {
8330c0d06caSMauro Carvalho Chehab 			u8 state = (data[20] & 0x80) ? 1 : 0;
8340c0d06caSMauro Carvalho Chehab 			if (sd->button_pressed != state) {
8350c0d06caSMauro Carvalho Chehab 				input_report_key(gspca_dev->input_dev,
8360c0d06caSMauro Carvalho Chehab 						 KEY_CAMERA, state);
8370c0d06caSMauro Carvalho Chehab 				input_sync(gspca_dev->input_dev);
8380c0d06caSMauro Carvalho Chehab 				sd->button_pressed = state;
8390c0d06caSMauro Carvalho Chehab 			}
8400c0d06caSMauro Carvalho Chehab 		}
8410c0d06caSMauro Carvalho Chehab #endif
8420c0d06caSMauro Carvalho Chehab 		/* Control Packet, after this came the header again,
8430c0d06caSMauro Carvalho Chehab 		 * but extra bytes came in the packet before this,
8440c0d06caSMauro Carvalho Chehab 		 * sometimes an EOF arrives, sometimes not... */
8450c0d06caSMauro Carvalho Chehab 		return;
8460c0d06caSMauro Carvalho Chehab 	}
8470c0d06caSMauro Carvalho Chehab 	data += 2;
8480c0d06caSMauro Carvalho Chehab 	len -= 2;
8490c0d06caSMauro Carvalho Chehab 	if (data[0] == 0xff && data[1] == 0xd8)
8500c0d06caSMauro Carvalho Chehab 		pkt_type = FIRST_PACKET;
8510c0d06caSMauro Carvalho Chehab 	else if (data[len - 2] == 0xff && data[len - 1] == 0xd9)
8520c0d06caSMauro Carvalho Chehab 		pkt_type = LAST_PACKET;
8530c0d06caSMauro Carvalho Chehab 	else
8540c0d06caSMauro Carvalho Chehab 		pkt_type = INTER_PACKET;
8550c0d06caSMauro Carvalho Chehab 	gspca_frame_add(gspca_dev, pkt_type, data, len);
8560c0d06caSMauro Carvalho Chehab }
8570c0d06caSMauro Carvalho Chehab 
sd_g_volatile_ctrl(struct v4l2_ctrl * ctrl)8580c0d06caSMauro Carvalho Chehab static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
8590c0d06caSMauro Carvalho Chehab {
8600c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev =
8610c0d06caSMauro Carvalho Chehab 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
8620c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
8630c0d06caSMauro Carvalho Chehab 	s32 red_gain, blue_gain, green_gain;
8640c0d06caSMauro Carvalho Chehab 
8650c0d06caSMauro Carvalho Chehab 	gspca_dev->usb_err = 0;
8660c0d06caSMauro Carvalho Chehab 
8670c0d06caSMauro Carvalho Chehab 	switch (ctrl->id) {
8680c0d06caSMauro Carvalho Chehab 	case V4L2_CID_AUTO_WHITE_BALANCE:
8690c0d06caSMauro Carvalho Chehab 		red_gain = reg_r(gspca_dev, 0x0087);
8700c0d06caSMauro Carvalho Chehab 		if (red_gain > 0x40)
8710c0d06caSMauro Carvalho Chehab 			red_gain = 0x40;
8720c0d06caSMauro Carvalho Chehab 		else if (red_gain < 0x10)
8730c0d06caSMauro Carvalho Chehab 			red_gain = 0x10;
8740c0d06caSMauro Carvalho Chehab 
8750c0d06caSMauro Carvalho Chehab 		blue_gain = reg_r(gspca_dev, 0x0088);
8760c0d06caSMauro Carvalho Chehab 		if (blue_gain > 0x40)
8770c0d06caSMauro Carvalho Chehab 			blue_gain = 0x40;
8780c0d06caSMauro Carvalho Chehab 		else if (blue_gain < 0x10)
8790c0d06caSMauro Carvalho Chehab 			blue_gain = 0x10;
8800c0d06caSMauro Carvalho Chehab 
8810c0d06caSMauro Carvalho Chehab 		green_gain = reg_r(gspca_dev, 0x0089);
8820c0d06caSMauro Carvalho Chehab 		if (green_gain > 0x40)
8830c0d06caSMauro Carvalho Chehab 			green_gain = 0x40;
8840c0d06caSMauro Carvalho Chehab 		else if (green_gain < 0x10)
8850c0d06caSMauro Carvalho Chehab 			green_gain = 0x10;
8860c0d06caSMauro Carvalho Chehab 
8870c0d06caSMauro Carvalho Chehab 		sd->gain->val = green_gain;
8880c0d06caSMauro Carvalho Chehab 		sd->red_balance->val = red_gain - green_gain;
8890c0d06caSMauro Carvalho Chehab 		sd->blue_balance->val = blue_gain - green_gain;
8900c0d06caSMauro Carvalho Chehab 		break;
8910c0d06caSMauro Carvalho Chehab 	}
8920c0d06caSMauro Carvalho Chehab 	return 0;
8930c0d06caSMauro Carvalho Chehab }
8940c0d06caSMauro Carvalho Chehab 
sd_s_ctrl(struct v4l2_ctrl * ctrl)8950c0d06caSMauro Carvalho Chehab static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
8960c0d06caSMauro Carvalho Chehab {
8970c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev =
8980c0d06caSMauro Carvalho Chehab 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
8990c0d06caSMauro Carvalho Chehab 
9000c0d06caSMauro Carvalho Chehab 	gspca_dev->usb_err = 0;
9010c0d06caSMauro Carvalho Chehab 
9020c0d06caSMauro Carvalho Chehab 	if (!gspca_dev->streaming)
9030c0d06caSMauro Carvalho Chehab 		return 0;
9040c0d06caSMauro Carvalho Chehab 
9050c0d06caSMauro Carvalho Chehab 	switch (ctrl->id) {
9060c0d06caSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
9070c0d06caSMauro Carvalho Chehab 		setbrightness(gspca_dev, ctrl->val);
9080c0d06caSMauro Carvalho Chehab 		break;
9090c0d06caSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
9100c0d06caSMauro Carvalho Chehab 		setcontrast(gspca_dev, ctrl->val);
9110c0d06caSMauro Carvalho Chehab 		break;
9120c0d06caSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
9130c0d06caSMauro Carvalho Chehab 		setcolors(gspca_dev, ctrl->val);
9140c0d06caSMauro Carvalho Chehab 		break;
9150c0d06caSMauro Carvalho Chehab 	case V4L2_CID_GAMMA:
9160c0d06caSMauro Carvalho Chehab 		setgamma(gspca_dev, ctrl->val);
9170c0d06caSMauro Carvalho Chehab 		break;
9180c0d06caSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
9190c0d06caSMauro Carvalho Chehab 		setmirror(gspca_dev, ctrl->val);
9200c0d06caSMauro Carvalho Chehab 		break;
9210c0d06caSMauro Carvalho Chehab 	case V4L2_CID_SHARPNESS:
9220c0d06caSMauro Carvalho Chehab 		setsharpness(gspca_dev, ctrl->val);
9230c0d06caSMauro Carvalho Chehab 		break;
9240c0d06caSMauro Carvalho Chehab 	case V4L2_CID_POWER_LINE_FREQUENCY:
9250c0d06caSMauro Carvalho Chehab 		setfreq(gspca_dev, ctrl->val);
9260c0d06caSMauro Carvalho Chehab 		break;
9270c0d06caSMauro Carvalho Chehab 	case V4L2_CID_BACKLIGHT_COMPENSATION:
9280c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e);
9290c0d06caSMauro Carvalho Chehab 		break;
9300c0d06caSMauro Carvalho Chehab 	case V4L2_CID_AUTO_WHITE_BALANCE:
9310c0d06caSMauro Carvalho Chehab 		setawb_n_RGB(gspca_dev);
9320c0d06caSMauro Carvalho Chehab 		break;
9330c0d06caSMauro Carvalho Chehab 	case V4L2_CID_COLORFX:
9340c0d06caSMauro Carvalho Chehab 		seteffect(gspca_dev, ctrl->val);
9350c0d06caSMauro Carvalho Chehab 		break;
9360c0d06caSMauro Carvalho Chehab 	}
9370c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
9380c0d06caSMauro Carvalho Chehab }
9390c0d06caSMauro Carvalho Chehab 
9400c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops sd_ctrl_ops = {
9410c0d06caSMauro Carvalho Chehab 	.g_volatile_ctrl = sd_g_volatile_ctrl,
9420c0d06caSMauro Carvalho Chehab 	.s_ctrl = sd_s_ctrl,
9430c0d06caSMauro Carvalho Chehab };
9440c0d06caSMauro Carvalho Chehab 
sd_init_controls(struct gspca_dev * gspca_dev)9450c0d06caSMauro Carvalho Chehab static int sd_init_controls(struct gspca_dev *gspca_dev)
9460c0d06caSMauro Carvalho Chehab {
9470c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
9480c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
9490c0d06caSMauro Carvalho Chehab 
9500c0d06caSMauro Carvalho Chehab 	gspca_dev->vdev.ctrl_handler = hdl;
9510c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(hdl, 12);
9520c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9530c0d06caSMauro Carvalho Chehab 			V4L2_CID_BRIGHTNESS, 0, 14, 1, 8);
9540c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9550c0d06caSMauro Carvalho Chehab 			V4L2_CID_CONTRAST, 0, 0x0d, 1, 7);
9560c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9570c0d06caSMauro Carvalho Chehab 			V4L2_CID_SATURATION, 0, 0xf, 1, 5);
9580c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9590c0d06caSMauro Carvalho Chehab 			V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10);
9603e4d8f48SMauro Carvalho Chehab 	/* Activate lowlight, some apps don't bring up the
9610c0d06caSMauro Carvalho Chehab 	   backlight_compensation control) */
9620c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9630c0d06caSMauro Carvalho Chehab 			V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1);
9640c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_TAS5130A)
9650c0d06caSMauro Carvalho Chehab 		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9660c0d06caSMauro Carvalho Chehab 				V4L2_CID_HFLIP, 0, 1, 1, 0);
9670c0d06caSMauro Carvalho Chehab 	sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9680c0d06caSMauro Carvalho Chehab 			V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
9690c0d06caSMauro Carvalho Chehab 	sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9700c0d06caSMauro Carvalho Chehab 			V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20);
9710c0d06caSMauro Carvalho Chehab 	sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9720c0d06caSMauro Carvalho Chehab 			V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0);
9730c0d06caSMauro Carvalho Chehab 	sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9740c0d06caSMauro Carvalho Chehab 			V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0);
9750c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9760c0d06caSMauro Carvalho Chehab 			V4L2_CID_SHARPNESS, 0, 15, 1, 6);
9770c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
9780c0d06caSMauro Carvalho Chehab 			V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH,
9790c0d06caSMauro Carvalho Chehab 			~((1 << V4L2_COLORFX_NONE) |
9800c0d06caSMauro Carvalho Chehab 			  (1 << V4L2_COLORFX_BW) |
9810c0d06caSMauro Carvalho Chehab 			  (1 << V4L2_COLORFX_SEPIA) |
9820c0d06caSMauro Carvalho Chehab 			  (1 << V4L2_COLORFX_SKETCH) |
9830c0d06caSMauro Carvalho Chehab 			  (1 << V4L2_COLORFX_NEGATIVE)),
9840c0d06caSMauro Carvalho Chehab 			V4L2_COLORFX_NONE);
9850c0d06caSMauro Carvalho Chehab 	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
9860c0d06caSMauro Carvalho Chehab 			V4L2_CID_POWER_LINE_FREQUENCY,
9870c0d06caSMauro Carvalho Chehab 			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
9880c0d06caSMauro Carvalho Chehab 			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
9890c0d06caSMauro Carvalho Chehab 
9900c0d06caSMauro Carvalho Chehab 	if (hdl->error) {
9910c0d06caSMauro Carvalho Chehab 		pr_err("Could not initialize controls\n");
9920c0d06caSMauro Carvalho Chehab 		return hdl->error;
9930c0d06caSMauro Carvalho Chehab 	}
9940c0d06caSMauro Carvalho Chehab 
9950c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true);
9960c0d06caSMauro Carvalho Chehab 
9970c0d06caSMauro Carvalho Chehab 	return 0;
9980c0d06caSMauro Carvalho Chehab }
9990c0d06caSMauro Carvalho Chehab 
10000c0d06caSMauro Carvalho Chehab /* sub-driver description */
10010c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc = {
10020c0d06caSMauro Carvalho Chehab 	.name = MODULE_NAME,
10030c0d06caSMauro Carvalho Chehab 	.config = sd_config,
10040c0d06caSMauro Carvalho Chehab 	.init = sd_init,
10050c0d06caSMauro Carvalho Chehab 	.init_controls = sd_init_controls,
10060c0d06caSMauro Carvalho Chehab 	.start = sd_start,
10070c0d06caSMauro Carvalho Chehab 	.stopN = sd_stopN,
10080c0d06caSMauro Carvalho Chehab 	.pkt_scan = sd_pkt_scan,
1009f8d26879SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT)
10100c0d06caSMauro Carvalho Chehab 	.other_input = 1,
10110c0d06caSMauro Carvalho Chehab #endif
10120c0d06caSMauro Carvalho Chehab };
10130c0d06caSMauro Carvalho Chehab 
10140c0d06caSMauro Carvalho Chehab /* -- module initialisation -- */
10150c0d06caSMauro Carvalho Chehab static const struct usb_device_id device_table[] = {
10160c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x17a1, 0x0128)},
10170c0d06caSMauro Carvalho Chehab 	{}
10180c0d06caSMauro Carvalho Chehab };
10190c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table);
10200c0d06caSMauro Carvalho Chehab 
10210c0d06caSMauro Carvalho Chehab /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)10220c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf,
10230c0d06caSMauro Carvalho Chehab 		    const struct usb_device_id *id)
10240c0d06caSMauro Carvalho Chehab {
10250c0d06caSMauro Carvalho Chehab 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
10260c0d06caSMauro Carvalho Chehab 			       THIS_MODULE);
10270c0d06caSMauro Carvalho Chehab }
10280c0d06caSMauro Carvalho Chehab 
10290c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = {
10300c0d06caSMauro Carvalho Chehab 	.name = MODULE_NAME,
10310c0d06caSMauro Carvalho Chehab 	.id_table = device_table,
10320c0d06caSMauro Carvalho Chehab 	.probe = sd_probe,
10330c0d06caSMauro Carvalho Chehab 	.disconnect = gspca_disconnect,
10340c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
10350c0d06caSMauro Carvalho Chehab 	.suspend = gspca_suspend,
10360c0d06caSMauro Carvalho Chehab 	.resume = gspca_resume,
10370c0d06caSMauro Carvalho Chehab 	.reset_resume = gspca_resume,
10380c0d06caSMauro Carvalho Chehab #endif
10390c0d06caSMauro Carvalho Chehab };
10400c0d06caSMauro Carvalho Chehab 
10410c0d06caSMauro Carvalho Chehab module_usb_driver(sd_driver);
1042