xref: /linux/drivers/media/usb/gspca/sq930x.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
1fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * SQ930x subdriver
40c0d06caSMauro Carvalho Chehab  *
50c0d06caSMauro Carvalho Chehab  * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>
60c0d06caSMauro Carvalho Chehab  * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>
70c0d06caSMauro Carvalho Chehab  * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
80c0d06caSMauro Carvalho Chehab  */
90c0d06caSMauro Carvalho Chehab 
100c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
110c0d06caSMauro Carvalho Chehab 
120c0d06caSMauro Carvalho Chehab #define MODULE_NAME "sq930x"
130c0d06caSMauro Carvalho Chehab 
140c0d06caSMauro Carvalho Chehab #include "gspca.h"
150c0d06caSMauro Carvalho Chehab 
160c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
170c0d06caSMauro Carvalho Chehab 		"Gerard Klaver <gerard at gkall dot hobby dot nl\n"
180c0d06caSMauro Carvalho Chehab 		"Sam Revitch <samr7@cs.washington.edu>");
190c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
200c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
210c0d06caSMauro Carvalho Chehab 
220c0d06caSMauro Carvalho Chehab /* Structure to hold all of our device specific stuff */
230c0d06caSMauro Carvalho Chehab struct sd {
240c0d06caSMauro Carvalho Chehab 	struct gspca_dev gspca_dev;	/* !! must be the first item */
250c0d06caSMauro Carvalho Chehab 
260c0d06caSMauro Carvalho Chehab 	struct { /* exposure/gain control cluster */
270c0d06caSMauro Carvalho Chehab 		struct v4l2_ctrl *exposure;
280c0d06caSMauro Carvalho Chehab 		struct v4l2_ctrl *gain;
290c0d06caSMauro Carvalho Chehab 	};
300c0d06caSMauro Carvalho Chehab 
310c0d06caSMauro Carvalho Chehab 	u8 do_ctrl;
320c0d06caSMauro Carvalho Chehab 	u8 gpio[2];
330c0d06caSMauro Carvalho Chehab 	u8 sensor;
340c0d06caSMauro Carvalho Chehab 	u8 type;
350c0d06caSMauro Carvalho Chehab #define Generic 0
360c0d06caSMauro Carvalho Chehab #define Creative_live_motion 1
370c0d06caSMauro Carvalho Chehab };
380c0d06caSMauro Carvalho Chehab enum sensors {
390c0d06caSMauro Carvalho Chehab 	SENSOR_ICX098BQ,
400c0d06caSMauro Carvalho Chehab 	SENSOR_LZ24BP,
410c0d06caSMauro Carvalho Chehab 	SENSOR_MI0360,
420c0d06caSMauro Carvalho Chehab 	SENSOR_MT9V111,		/* = MI360SOC */
430c0d06caSMauro Carvalho Chehab 	SENSOR_OV7660,
440c0d06caSMauro Carvalho Chehab 	SENSOR_OV9630,
450c0d06caSMauro Carvalho Chehab };
460c0d06caSMauro Carvalho Chehab 
470c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format vga_mode[] = {
480c0d06caSMauro Carvalho Chehab 	{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
490c0d06caSMauro Carvalho Chehab 		.bytesperline = 320,
500c0d06caSMauro Carvalho Chehab 		.sizeimage = 320 * 240,
510c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
520c0d06caSMauro Carvalho Chehab 		.priv = 0},
530c0d06caSMauro Carvalho Chehab 	{640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
540c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
550c0d06caSMauro Carvalho Chehab 		.sizeimage = 640 * 480,
560c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
570c0d06caSMauro Carvalho Chehab 		.priv = 1},
580c0d06caSMauro Carvalho Chehab };
590c0d06caSMauro Carvalho Chehab 
600c0d06caSMauro Carvalho Chehab /* sq930x registers */
610c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_UCBUS_IO	0x0001
620c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_I2C_IO	0x0002
630c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_GPIO		0x0005
640c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_CAP_START	0x0010
650c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_CAP_STOP	0x0011
660c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_SET_EXPOSURE 0x001d
670c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_RESET	0x001e
680c0d06caSMauro Carvalho Chehab #define SQ930_CTRL_GET_DEV_INFO 0x001f
690c0d06caSMauro Carvalho Chehab 
700c0d06caSMauro Carvalho Chehab /* gpio 1 (8..15) */
710c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_DFL_I2C_SDA	0x0001
720c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_DFL_I2C_SCL	0x0002
730c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_RSTBAR	0x0004
740c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_EXTRA1	0x0040
750c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_EXTRA2	0x0080
760c0d06caSMauro Carvalho Chehab /* gpio 3 (24..31) */
770c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_POWER	0x0200
780c0d06caSMauro Carvalho Chehab #define SQ930_GPIO_DFL_LED	0x1000
790c0d06caSMauro Carvalho Chehab 
800c0d06caSMauro Carvalho Chehab struct ucbus_write_cmd {
810c0d06caSMauro Carvalho Chehab 	u16	bw_addr;
820c0d06caSMauro Carvalho Chehab 	u8	bw_data;
830c0d06caSMauro Carvalho Chehab };
840c0d06caSMauro Carvalho Chehab struct i2c_write_cmd {
850c0d06caSMauro Carvalho Chehab 	u8	reg;
860c0d06caSMauro Carvalho Chehab 	u16	val;
870c0d06caSMauro Carvalho Chehab };
880c0d06caSMauro Carvalho Chehab 
890c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd icx098bq_start_0[] = {
900c0d06caSMauro Carvalho Chehab 	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},
910c0d06caSMauro Carvalho Chehab 	{0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},
920c0d06caSMauro Carvalho Chehab 	{0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},
930c0d06caSMauro Carvalho Chehab 	{0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},
940c0d06caSMauro Carvalho Chehab 	{0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},
950c0d06caSMauro Carvalho Chehab 	{0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},
960c0d06caSMauro Carvalho Chehab 	{0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},
970c0d06caSMauro Carvalho Chehab 	{0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},
980c0d06caSMauro Carvalho Chehab 	{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
990c0d06caSMauro Carvalho Chehab 	{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
1000c0d06caSMauro Carvalho Chehab 	{0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},
1010c0d06caSMauro Carvalho Chehab 	{0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
1020c0d06caSMauro Carvalho Chehab 	{0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},
1030c0d06caSMauro Carvalho Chehab 	{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
1040c0d06caSMauro Carvalho Chehab 	{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
1050c0d06caSMauro Carvalho Chehab 	{0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},
1060c0d06caSMauro Carvalho Chehab 	{0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},
1070c0d06caSMauro Carvalho Chehab 	{0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},
1080c0d06caSMauro Carvalho Chehab 	{0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},
1090c0d06caSMauro Carvalho Chehab 	{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
1100c0d06caSMauro Carvalho Chehab 	{0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},
1110c0d06caSMauro Carvalho Chehab 	{0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},
1120c0d06caSMauro Carvalho Chehab 	{0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},
1130c0d06caSMauro Carvalho Chehab 	{0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},
1140c0d06caSMauro Carvalho Chehab 	{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
1150c0d06caSMauro Carvalho Chehab 	{0xf800, 0x03}
1160c0d06caSMauro Carvalho Chehab };
1170c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd icx098bq_start_1[] = {
1180c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
1190c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xc0},
1200c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
1210c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xc0},
1220c0d06caSMauro Carvalho Chehab 	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
1230c0d06caSMauro Carvalho Chehab 	{0xf5f9, 0x00}
1240c0d06caSMauro Carvalho Chehab };
1250c0d06caSMauro Carvalho Chehab 
1260c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd icx098bq_start_2[] = {
1270c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},
1280c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03},
1290c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},
1300c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03},
1310c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},
1320c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03},
1330c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
1340c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03}
1350c0d06caSMauro Carvalho Chehab };
1360c0d06caSMauro Carvalho Chehab 
1370c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd lz24bp_start_0[] = {
1380c0d06caSMauro Carvalho Chehab 	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},
1390c0d06caSMauro Carvalho Chehab 	{0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},
1400c0d06caSMauro Carvalho Chehab 	{0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},
1410c0d06caSMauro Carvalho Chehab 	{0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},
1420c0d06caSMauro Carvalho Chehab 	{0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},
1430c0d06caSMauro Carvalho Chehab 	{0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},
1440c0d06caSMauro Carvalho Chehab 	{0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},
1450c0d06caSMauro Carvalho Chehab 	{0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},
1460c0d06caSMauro Carvalho Chehab 	{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
1470c0d06caSMauro Carvalho Chehab 	{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
1480c0d06caSMauro Carvalho Chehab 	{0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},
1490c0d06caSMauro Carvalho Chehab 	{0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
1500c0d06caSMauro Carvalho Chehab 	{0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},
1510c0d06caSMauro Carvalho Chehab 	{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
1520c0d06caSMauro Carvalho Chehab 	{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
1530c0d06caSMauro Carvalho Chehab 	{0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},
1540c0d06caSMauro Carvalho Chehab 	{0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},
1550c0d06caSMauro Carvalho Chehab 	{0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},
1560c0d06caSMauro Carvalho Chehab 	{0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},
1570c0d06caSMauro Carvalho Chehab 	{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
1580c0d06caSMauro Carvalho Chehab 	{0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},
1590c0d06caSMauro Carvalho Chehab 	{0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},
1600c0d06caSMauro Carvalho Chehab 	{0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},
1610c0d06caSMauro Carvalho Chehab 	{0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},
1620c0d06caSMauro Carvalho Chehab 	{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
1630c0d06caSMauro Carvalho Chehab 	{0xf800, 0x03}
1640c0d06caSMauro Carvalho Chehab };
1650c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
1660c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
1670c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xb3},
1680c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
1690c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xb3},
1700c0d06caSMauro Carvalho Chehab 	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
1710c0d06caSMauro Carvalho Chehab 	{0xf5f9, 0x00}
1720c0d06caSMauro Carvalho Chehab };
1730c0d06caSMauro Carvalho Chehab 
1740c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
1750c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
1760c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xc0},
1770c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
1780c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xc0},
1790c0d06caSMauro Carvalho Chehab 	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
1800c0d06caSMauro Carvalho Chehab 	{0xf5f9, 0x00}
1810c0d06caSMauro Carvalho Chehab };
1820c0d06caSMauro Carvalho Chehab 
1830c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd lz24bp_start_2[] = {
1840c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},
1850c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03},
1860c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},
1870c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03},
1880c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},
1890c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03},
1900c0d06caSMauro Carvalho Chehab 	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
1910c0d06caSMauro Carvalho Chehab 	{0xf807, 0x7f}, {0xf800, 0x03}
1920c0d06caSMauro Carvalho Chehab };
1930c0d06caSMauro Carvalho Chehab 
1940c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd mi0360_start_0[] = {
1950c0d06caSMauro Carvalho Chehab 	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},
1960c0d06caSMauro Carvalho Chehab 	{0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}
1970c0d06caSMauro Carvalho Chehab };
1980c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mi0360_init_23[] = {
1990c0d06caSMauro Carvalho Chehab 	{0x30, 0x0040},		/* reserved - def 0x0005 */
2000c0d06caSMauro Carvalho Chehab 	{0x31, 0x0000},		/* reserved - def 0x002a */
2010c0d06caSMauro Carvalho Chehab 	{0x34, 0x0100},		/* reserved - def 0x0100 */
2020c0d06caSMauro Carvalho Chehab 	{0x3d, 0x068f},		/* reserved - def 0x068f */
2030c0d06caSMauro Carvalho Chehab };
2040c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mi0360_init_24[] = {
2050c0d06caSMauro Carvalho Chehab 	{0x03, 0x01e5},		/* window height */
2060c0d06caSMauro Carvalho Chehab 	{0x04, 0x0285},		/* window width */
2070c0d06caSMauro Carvalho Chehab };
2080c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mi0360_init_25[] = {
2090c0d06caSMauro Carvalho Chehab 	{0x35, 0x0020},		/* global gain */
2100c0d06caSMauro Carvalho Chehab 	{0x2b, 0x0020},		/* green1 gain */
2110c0d06caSMauro Carvalho Chehab 	{0x2c, 0x002a},		/* blue gain */
2120c0d06caSMauro Carvalho Chehab 	{0x2d, 0x0028},		/* red gain */
2130c0d06caSMauro Carvalho Chehab 	{0x2e, 0x0020},		/* green2 gain */
2140c0d06caSMauro Carvalho Chehab };
2150c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd mi0360_start_1[] = {
2160c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
2170c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xa6},
2180c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
2190c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xa6},
2200c0d06caSMauro Carvalho Chehab 	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
2210c0d06caSMauro Carvalho Chehab 	{0xf5f9, 0x00}
2220c0d06caSMauro Carvalho Chehab };
2230c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mi0360_start_2[] = {
2240c0d06caSMauro Carvalho Chehab 	{0x62, 0x041d},		/* reserved - def 0x0418 */
2250c0d06caSMauro Carvalho Chehab };
2260c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mi0360_start_3[] = {
2270c0d06caSMauro Carvalho Chehab 	{0x05, 0x007b},		/* horiz blanking */
2280c0d06caSMauro Carvalho Chehab };
2290c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mi0360_start_4[] = {
2300c0d06caSMauro Carvalho Chehab 	{0x05, 0x03f5},		/* horiz blanking */
2310c0d06caSMauro Carvalho Chehab };
2320c0d06caSMauro Carvalho Chehab 
2330c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mt9v111_init_0[] = {
2340c0d06caSMauro Carvalho Chehab 	{0x01, 0x0001},		/* select IFP/SOC registers */
2350c0d06caSMauro Carvalho Chehab 	{0x06, 0x300c},		/* operating mode control */
2360c0d06caSMauro Carvalho Chehab 	{0x08, 0xcc00},		/* output format control (RGB) */
2370c0d06caSMauro Carvalho Chehab 	{0x01, 0x0004},		/* select sensor core registers */
2380c0d06caSMauro Carvalho Chehab };
2390c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mt9v111_init_1[] = {
2400c0d06caSMauro Carvalho Chehab 	{0x03, 0x01e5},		/* window height */
2410c0d06caSMauro Carvalho Chehab 	{0x04, 0x0285},		/* window width */
2420c0d06caSMauro Carvalho Chehab };
2430c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mt9v111_init_2[] = {
2440c0d06caSMauro Carvalho Chehab 	{0x30, 0x7800},
2450c0d06caSMauro Carvalho Chehab 	{0x31, 0x0000},
2460c0d06caSMauro Carvalho Chehab 	{0x07, 0x3002},		/* output control */
2470c0d06caSMauro Carvalho Chehab 	{0x35, 0x0020},		/* global gain */
2480c0d06caSMauro Carvalho Chehab 	{0x2b, 0x0020},		/* green1 gain */
2490c0d06caSMauro Carvalho Chehab 	{0x2c, 0x0020},		/* blue gain */
2500c0d06caSMauro Carvalho Chehab 	{0x2d, 0x0020},		/* red gain */
2510c0d06caSMauro Carvalho Chehab 	{0x2e, 0x0020},		/* green2 gain */
2520c0d06caSMauro Carvalho Chehab };
2530c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd mt9v111_start_1[] = {
2540c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
2550c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xaa},
2560c0d06caSMauro Carvalho Chehab 	{0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
2570c0d06caSMauro Carvalho Chehab 	{0xf5f4, 0xaa},
2580c0d06caSMauro Carvalho Chehab 	{0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},
2590c0d06caSMauro Carvalho Chehab 	{0xf5f9, 0x0a}
2600c0d06caSMauro Carvalho Chehab };
2610c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mt9v111_init_3[] = {
2620c0d06caSMauro Carvalho Chehab 	{0x62, 0x0405},
2630c0d06caSMauro Carvalho Chehab };
2640c0d06caSMauro Carvalho Chehab static const struct i2c_write_cmd mt9v111_init_4[] = {
2650c0d06caSMauro Carvalho Chehab /*	{0x05, 0x00ce}, */
2660c0d06caSMauro Carvalho Chehab 	{0x05, 0x005d},		/* horizontal blanking */
2670c0d06caSMauro Carvalho Chehab };
2680c0d06caSMauro Carvalho Chehab 
2690c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd ov7660_start_0[] = {
2700c0d06caSMauro Carvalho Chehab 	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},
2710c0d06caSMauro Carvalho Chehab 	{0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}
2720c0d06caSMauro Carvalho Chehab };
2730c0d06caSMauro Carvalho Chehab 
2740c0d06caSMauro Carvalho Chehab static const struct ucbus_write_cmd ov9630_start_0[] = {
2750c0d06caSMauro Carvalho Chehab 	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},
2760c0d06caSMauro Carvalho Chehab 	{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
2770c0d06caSMauro Carvalho Chehab };
2780c0d06caSMauro Carvalho Chehab 
2790c0d06caSMauro Carvalho Chehab /* start parameters indexed by [sensor][mode] */
2800c0d06caSMauro Carvalho Chehab static const struct cap_s {
2810c0d06caSMauro Carvalho Chehab 	u8	cc_sizeid;
2820c0d06caSMauro Carvalho Chehab 	u8	cc_bytes[32];
2830c0d06caSMauro Carvalho Chehab } capconfig[4][2] = {
2840c0d06caSMauro Carvalho Chehab 	[SENSOR_ICX098BQ] = {
2850c0d06caSMauro Carvalho Chehab 		{2,				/* Bayer 320x240 */
2860c0d06caSMauro Carvalho Chehab 		  {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
2870c0d06caSMauro Carvalho Chehab 		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
2880c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
2890c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
2900c0d06caSMauro Carvalho Chehab 		{4,				/* Bayer 640x480 */
2910c0d06caSMauro Carvalho Chehab 		  {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
2920c0d06caSMauro Carvalho Chehab 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
2930c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2940c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
2950c0d06caSMauro Carvalho Chehab 	},
2960c0d06caSMauro Carvalho Chehab 	[SENSOR_LZ24BP] = {
2970c0d06caSMauro Carvalho Chehab 		{2,				/* Bayer 320x240 */
2980c0d06caSMauro Carvalho Chehab 		  {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
2990c0d06caSMauro Carvalho Chehab 		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
3000c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3010c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
3020c0d06caSMauro Carvalho Chehab 		{4,				/* Bayer 640x480 */
3030c0d06caSMauro Carvalho Chehab 		  {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
3040c0d06caSMauro Carvalho Chehab 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
3050c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3060c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
3070c0d06caSMauro Carvalho Chehab 	},
3080c0d06caSMauro Carvalho Chehab 	[SENSOR_MI0360] = {
3090c0d06caSMauro Carvalho Chehab 		{2,				/* Bayer 320x240 */
3100c0d06caSMauro Carvalho Chehab 		  {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
3110c0d06caSMauro Carvalho Chehab 		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
3120c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3130c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
3140c0d06caSMauro Carvalho Chehab 		{4,				/* Bayer 640x480 */
3150c0d06caSMauro Carvalho Chehab 		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
3160c0d06caSMauro Carvalho Chehab 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
3170c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3180c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
3190c0d06caSMauro Carvalho Chehab 	},
3200c0d06caSMauro Carvalho Chehab 	[SENSOR_MT9V111] = {
3210c0d06caSMauro Carvalho Chehab 		{2,				/* Bayer 320x240 */
3220c0d06caSMauro Carvalho Chehab 		  {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
3230c0d06caSMauro Carvalho Chehab 		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
3240c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3250c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
3260c0d06caSMauro Carvalho Chehab 		{4,				/* Bayer 640x480 */
3270c0d06caSMauro Carvalho Chehab 		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
3280c0d06caSMauro Carvalho Chehab 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
3290c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3300c0d06caSMauro Carvalho Chehab 		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
3310c0d06caSMauro Carvalho Chehab 	},
3320c0d06caSMauro Carvalho Chehab };
3330c0d06caSMauro Carvalho Chehab 
3340c0d06caSMauro Carvalho Chehab struct sensor_s {
3350c0d06caSMauro Carvalho Chehab 	const char *name;
3360c0d06caSMauro Carvalho Chehab 	u8 i2c_addr;
3370c0d06caSMauro Carvalho Chehab 	u8 i2c_dum;
3380c0d06caSMauro Carvalho Chehab 	u8 gpio[5];
3390c0d06caSMauro Carvalho Chehab 	u8 cmd_len;
3400c0d06caSMauro Carvalho Chehab 	const struct ucbus_write_cmd *cmd;
3410c0d06caSMauro Carvalho Chehab };
3420c0d06caSMauro Carvalho Chehab 
3430c0d06caSMauro Carvalho Chehab static const struct sensor_s sensor_tb[] = {
3440c0d06caSMauro Carvalho Chehab 	[SENSOR_ICX098BQ] = {
3450c0d06caSMauro Carvalho Chehab 		"icx098bp",
3460c0d06caSMauro Carvalho Chehab 		0x00, 0x00,
3470c0d06caSMauro Carvalho Chehab 		{0,
3480c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
3490c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA,
3500c0d06caSMauro Carvalho Chehab 		 0,
3510c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_RSTBAR
3520c0d06caSMauro Carvalho Chehab 		},
3530c0d06caSMauro Carvalho Chehab 		8, icx098bq_start_0
3540c0d06caSMauro Carvalho Chehab 	    },
3550c0d06caSMauro Carvalho Chehab 	[SENSOR_LZ24BP] = {
3560c0d06caSMauro Carvalho Chehab 		"lz24bp",
3570c0d06caSMauro Carvalho Chehab 		0x00, 0x00,
3580c0d06caSMauro Carvalho Chehab 		{0,
3590c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
3600c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA,
3610c0d06caSMauro Carvalho Chehab 		 0,
3620c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_RSTBAR
3630c0d06caSMauro Carvalho Chehab 		},
3640c0d06caSMauro Carvalho Chehab 		8, lz24bp_start_0
3650c0d06caSMauro Carvalho Chehab 	    },
3660c0d06caSMauro Carvalho Chehab 	[SENSOR_MI0360] = {
3670c0d06caSMauro Carvalho Chehab 		"mi0360",
3680c0d06caSMauro Carvalho Chehab 		0x5d, 0x80,
3690c0d06caSMauro Carvalho Chehab 		{SQ930_GPIO_RSTBAR,
3700c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
3710c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA,
3720c0d06caSMauro Carvalho Chehab 		 0,
3730c0d06caSMauro Carvalho Chehab 		 0
3740c0d06caSMauro Carvalho Chehab 		},
3750c0d06caSMauro Carvalho Chehab 		7, mi0360_start_0
3760c0d06caSMauro Carvalho Chehab 	    },
3770c0d06caSMauro Carvalho Chehab 	[SENSOR_MT9V111] = {
3780c0d06caSMauro Carvalho Chehab 		"mt9v111",
3790c0d06caSMauro Carvalho Chehab 		0x5c, 0x7f,
3800c0d06caSMauro Carvalho Chehab 		{SQ930_GPIO_RSTBAR,
3810c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
3820c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA,
3830c0d06caSMauro Carvalho Chehab 		 0,
3840c0d06caSMauro Carvalho Chehab 		 0
3850c0d06caSMauro Carvalho Chehab 		},
3860c0d06caSMauro Carvalho Chehab 		7, mi0360_start_0
3870c0d06caSMauro Carvalho Chehab 	    },
3880c0d06caSMauro Carvalho Chehab 	[SENSOR_OV7660] = {
3890c0d06caSMauro Carvalho Chehab 		"ov7660",
3900c0d06caSMauro Carvalho Chehab 		0x21, 0x00,
3910c0d06caSMauro Carvalho Chehab 		{0,
3920c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
3930c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA,
3940c0d06caSMauro Carvalho Chehab 		 0,
3950c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_RSTBAR
3960c0d06caSMauro Carvalho Chehab 		},
3970c0d06caSMauro Carvalho Chehab 		7, ov7660_start_0
3980c0d06caSMauro Carvalho Chehab 	    },
3990c0d06caSMauro Carvalho Chehab 	[SENSOR_OV9630] = {
4000c0d06caSMauro Carvalho Chehab 		"ov9630",
4010c0d06caSMauro Carvalho Chehab 		0x30, 0x00,
4020c0d06caSMauro Carvalho Chehab 		{0,
4030c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
4040c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_DFL_I2C_SDA,
4050c0d06caSMauro Carvalho Chehab 		 0,
4060c0d06caSMauro Carvalho Chehab 		 SQ930_GPIO_RSTBAR
4070c0d06caSMauro Carvalho Chehab 		},
4080c0d06caSMauro Carvalho Chehab 		7, ov9630_start_0
4090c0d06caSMauro Carvalho Chehab 	    },
4100c0d06caSMauro Carvalho Chehab };
4110c0d06caSMauro Carvalho Chehab 
reg_r(struct gspca_dev * gspca_dev,u16 value,int len)4120c0d06caSMauro Carvalho Chehab static void reg_r(struct gspca_dev *gspca_dev,
4130c0d06caSMauro Carvalho Chehab 		u16 value, int len)
4140c0d06caSMauro Carvalho Chehab {
4150c0d06caSMauro Carvalho Chehab 	int ret;
4160c0d06caSMauro Carvalho Chehab 
4170c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
4180c0d06caSMauro Carvalho Chehab 		return;
4190c0d06caSMauro Carvalho Chehab 	ret = usb_control_msg(gspca_dev->dev,
4200c0d06caSMauro Carvalho Chehab 			usb_rcvctrlpipe(gspca_dev->dev, 0),
4210c0d06caSMauro Carvalho Chehab 			0x0c,
4220c0d06caSMauro Carvalho Chehab 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
4230c0d06caSMauro Carvalho Chehab 			value, 0, gspca_dev->usb_buf, len,
4240c0d06caSMauro Carvalho Chehab 			500);
4250c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
4260c0d06caSMauro Carvalho Chehab 		pr_err("reg_r %04x failed %d\n", value, ret);
4270c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = ret;
428*4843a543SHans Verkuil 		/*
429*4843a543SHans Verkuil 		 * Make sure the buffer is zeroed to avoid uninitialized
430*4843a543SHans Verkuil 		 * values.
431*4843a543SHans Verkuil 		 */
432*4843a543SHans Verkuil 		memset(gspca_dev->usb_buf, 0, USB_BUF_SZ);
4330c0d06caSMauro Carvalho Chehab 	}
4340c0d06caSMauro Carvalho Chehab }
4350c0d06caSMauro Carvalho Chehab 
reg_w(struct gspca_dev * gspca_dev,u16 value,u16 index)4360c0d06caSMauro Carvalho Chehab static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
4370c0d06caSMauro Carvalho Chehab {
4380c0d06caSMauro Carvalho Chehab 	int ret;
4390c0d06caSMauro Carvalho Chehab 
4400c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
4410c0d06caSMauro Carvalho Chehab 		return;
44237d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_USBO, "reg_w v: %04x i: %04x\n", value, index);
4430c0d06caSMauro Carvalho Chehab 	ret = usb_control_msg(gspca_dev->dev,
4440c0d06caSMauro Carvalho Chehab 			usb_sndctrlpipe(gspca_dev->dev, 0),
4450c0d06caSMauro Carvalho Chehab 			0x0c,			/* request */
4460c0d06caSMauro Carvalho Chehab 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
4470c0d06caSMauro Carvalho Chehab 			value, index, NULL, 0,
4480c0d06caSMauro Carvalho Chehab 			500);
4490c0d06caSMauro Carvalho Chehab 	msleep(30);
4500c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
4510c0d06caSMauro Carvalho Chehab 		pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
4520c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = ret;
4530c0d06caSMauro Carvalho Chehab 	}
4540c0d06caSMauro Carvalho Chehab }
4550c0d06caSMauro Carvalho Chehab 
reg_wb(struct gspca_dev * gspca_dev,u16 value,u16 index,const u8 * data,int len)4560c0d06caSMauro Carvalho Chehab static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
4570c0d06caSMauro Carvalho Chehab 		const u8 *data, int len)
4580c0d06caSMauro Carvalho Chehab {
4590c0d06caSMauro Carvalho Chehab 	int ret;
4600c0d06caSMauro Carvalho Chehab 
4610c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
4620c0d06caSMauro Carvalho Chehab 		return;
46337d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_USBO, "reg_wb v: %04x i: %04x %02x...%02x\n",
4640c0d06caSMauro Carvalho Chehab 		  value, index, *data, data[len - 1]);
4650c0d06caSMauro Carvalho Chehab 	memcpy(gspca_dev->usb_buf, data, len);
4660c0d06caSMauro Carvalho Chehab 	ret = usb_control_msg(gspca_dev->dev,
4670c0d06caSMauro Carvalho Chehab 			usb_sndctrlpipe(gspca_dev->dev, 0),
4680c0d06caSMauro Carvalho Chehab 			0x0c,			/* request */
4690c0d06caSMauro Carvalho Chehab 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
4700c0d06caSMauro Carvalho Chehab 			value, index, gspca_dev->usb_buf, len,
4710c0d06caSMauro Carvalho Chehab 			1000);
4720c0d06caSMauro Carvalho Chehab 	msleep(30);
4730c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
4740c0d06caSMauro Carvalho Chehab 		pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
4750c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = ret;
4760c0d06caSMauro Carvalho Chehab 	}
4770c0d06caSMauro Carvalho Chehab }
4780c0d06caSMauro Carvalho Chehab 
i2c_write(struct sd * sd,const struct i2c_write_cmd * cmd,int ncmds)4790c0d06caSMauro Carvalho Chehab static void i2c_write(struct sd *sd,
4800c0d06caSMauro Carvalho Chehab 			const struct i2c_write_cmd *cmd,
4810c0d06caSMauro Carvalho Chehab 			int ncmds)
4820c0d06caSMauro Carvalho Chehab {
4830c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev = &sd->gspca_dev;
4840c0d06caSMauro Carvalho Chehab 	const struct sensor_s *sensor;
4850c0d06caSMauro Carvalho Chehab 	u16 val, idx;
4860c0d06caSMauro Carvalho Chehab 	u8 *buf;
4870c0d06caSMauro Carvalho Chehab 	int ret;
4880c0d06caSMauro Carvalho Chehab 
4890c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
4900c0d06caSMauro Carvalho Chehab 		return;
4910c0d06caSMauro Carvalho Chehab 
4920c0d06caSMauro Carvalho Chehab 	sensor = &sensor_tb[sd->sensor];
4930c0d06caSMauro Carvalho Chehab 
4940c0d06caSMauro Carvalho Chehab 	val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;
4950c0d06caSMauro Carvalho Chehab 	idx = (cmd->val & 0xff00) | cmd->reg;
4960c0d06caSMauro Carvalho Chehab 
4970c0d06caSMauro Carvalho Chehab 	buf = gspca_dev->usb_buf;
4980c0d06caSMauro Carvalho Chehab 	*buf++ = sensor->i2c_dum;
4990c0d06caSMauro Carvalho Chehab 	*buf++ = cmd->val;
5000c0d06caSMauro Carvalho Chehab 
5010c0d06caSMauro Carvalho Chehab 	while (--ncmds > 0) {
5020c0d06caSMauro Carvalho Chehab 		cmd++;
5030c0d06caSMauro Carvalho Chehab 		*buf++ = cmd->reg;
5040c0d06caSMauro Carvalho Chehab 		*buf++ = cmd->val >> 8;
5050c0d06caSMauro Carvalho Chehab 		*buf++ = sensor->i2c_dum;
5060c0d06caSMauro Carvalho Chehab 		*buf++ = cmd->val;
5070c0d06caSMauro Carvalho Chehab 	}
5080c0d06caSMauro Carvalho Chehab 
50937d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_USBO, "i2c_w v: %04x i: %04x %02x...%02x\n",
5100c0d06caSMauro Carvalho Chehab 		  val, idx, gspca_dev->usb_buf[0], buf[-1]);
5110c0d06caSMauro Carvalho Chehab 	ret = usb_control_msg(gspca_dev->dev,
5120c0d06caSMauro Carvalho Chehab 			usb_sndctrlpipe(gspca_dev->dev, 0),
5130c0d06caSMauro Carvalho Chehab 			0x0c,			/* request */
5140c0d06caSMauro Carvalho Chehab 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
5150c0d06caSMauro Carvalho Chehab 			val, idx,
5160c0d06caSMauro Carvalho Chehab 			gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
5170c0d06caSMauro Carvalho Chehab 			500);
5180c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
5190c0d06caSMauro Carvalho Chehab 		pr_err("i2c_write failed %d\n", ret);
5200c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = ret;
5210c0d06caSMauro Carvalho Chehab 	}
5220c0d06caSMauro Carvalho Chehab }
5230c0d06caSMauro Carvalho Chehab 
ucbus_write(struct gspca_dev * gspca_dev,const struct ucbus_write_cmd * cmd,int ncmds,int batchsize)5240c0d06caSMauro Carvalho Chehab static void ucbus_write(struct gspca_dev *gspca_dev,
5250c0d06caSMauro Carvalho Chehab 			const struct ucbus_write_cmd *cmd,
5260c0d06caSMauro Carvalho Chehab 			int ncmds,
5270c0d06caSMauro Carvalho Chehab 			int batchsize)
5280c0d06caSMauro Carvalho Chehab {
5290c0d06caSMauro Carvalho Chehab 	u8 *buf;
5300c0d06caSMauro Carvalho Chehab 	u16 val, idx;
5310c0d06caSMauro Carvalho Chehab 	int len, ret;
5320c0d06caSMauro Carvalho Chehab 
5330c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
5340c0d06caSMauro Carvalho Chehab 		return;
5350c0d06caSMauro Carvalho Chehab 
5360c0d06caSMauro Carvalho Chehab 	if ((batchsize - 1) * 3 > USB_BUF_SZ) {
53752173c5fSJoe Perches 		gspca_err(gspca_dev, "Bug: usb_buf overflow\n");
5380c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = -ENOMEM;
5390c0d06caSMauro Carvalho Chehab 		return;
5400c0d06caSMauro Carvalho Chehab 	}
5410c0d06caSMauro Carvalho Chehab 
5420c0d06caSMauro Carvalho Chehab 	for (;;) {
5430c0d06caSMauro Carvalho Chehab 		len = ncmds;
5440c0d06caSMauro Carvalho Chehab 		if (len > batchsize)
5450c0d06caSMauro Carvalho Chehab 			len = batchsize;
5460c0d06caSMauro Carvalho Chehab 		ncmds -= len;
5470c0d06caSMauro Carvalho Chehab 
5480c0d06caSMauro Carvalho Chehab 		val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;
5490c0d06caSMauro Carvalho Chehab 		idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);
5500c0d06caSMauro Carvalho Chehab 
5510c0d06caSMauro Carvalho Chehab 		buf = gspca_dev->usb_buf;
5520c0d06caSMauro Carvalho Chehab 		while (--len > 0) {
5530c0d06caSMauro Carvalho Chehab 			cmd++;
5540c0d06caSMauro Carvalho Chehab 			*buf++ = cmd->bw_addr;
5550c0d06caSMauro Carvalho Chehab 			*buf++ = cmd->bw_addr >> 8;
5560c0d06caSMauro Carvalho Chehab 			*buf++ = cmd->bw_data;
5570c0d06caSMauro Carvalho Chehab 		}
5580c0d06caSMauro Carvalho Chehab 		if (buf != gspca_dev->usb_buf)
55937d5efb0SJoe Perches 			gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x %02x...%02x\n",
5600c0d06caSMauro Carvalho Chehab 				  val, idx,
5610c0d06caSMauro Carvalho Chehab 				  gspca_dev->usb_buf[0], buf[-1]);
5620c0d06caSMauro Carvalho Chehab 		else
56337d5efb0SJoe Perches 			gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x\n",
5640c0d06caSMauro Carvalho Chehab 				  val, idx);
5650c0d06caSMauro Carvalho Chehab 		ret = usb_control_msg(gspca_dev->dev,
5660c0d06caSMauro Carvalho Chehab 				usb_sndctrlpipe(gspca_dev->dev, 0),
5670c0d06caSMauro Carvalho Chehab 				0x0c,			/* request */
5680c0d06caSMauro Carvalho Chehab 			   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
5690c0d06caSMauro Carvalho Chehab 				val, idx,
5700c0d06caSMauro Carvalho Chehab 				gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
5710c0d06caSMauro Carvalho Chehab 				500);
5720c0d06caSMauro Carvalho Chehab 		if (ret < 0) {
5730c0d06caSMauro Carvalho Chehab 			pr_err("ucbus_write failed %d\n", ret);
5740c0d06caSMauro Carvalho Chehab 			gspca_dev->usb_err = ret;
5750c0d06caSMauro Carvalho Chehab 			return;
5760c0d06caSMauro Carvalho Chehab 		}
5770c0d06caSMauro Carvalho Chehab 		msleep(30);
5780c0d06caSMauro Carvalho Chehab 		if (ncmds <= 0)
5790c0d06caSMauro Carvalho Chehab 			break;
5800c0d06caSMauro Carvalho Chehab 		cmd++;
5810c0d06caSMauro Carvalho Chehab 	}
5820c0d06caSMauro Carvalho Chehab }
5830c0d06caSMauro Carvalho Chehab 
gpio_set(struct sd * sd,u16 val,u16 mask)5840c0d06caSMauro Carvalho Chehab static void gpio_set(struct sd *sd, u16 val, u16 mask)
5850c0d06caSMauro Carvalho Chehab {
5860c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev = &sd->gspca_dev;
5870c0d06caSMauro Carvalho Chehab 
5880c0d06caSMauro Carvalho Chehab 	if (mask & 0x00ff) {
5890c0d06caSMauro Carvalho Chehab 		sd->gpio[0] &= ~mask;
5900c0d06caSMauro Carvalho Chehab 		sd->gpio[0] |= val;
5910c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,
5920c0d06caSMauro Carvalho Chehab 			~sd->gpio[0] << 8);
5930c0d06caSMauro Carvalho Chehab 	}
5940c0d06caSMauro Carvalho Chehab 	mask >>= 8;
5950c0d06caSMauro Carvalho Chehab 	val >>= 8;
5960c0d06caSMauro Carvalho Chehab 	if (mask) {
5970c0d06caSMauro Carvalho Chehab 		sd->gpio[1] &= ~mask;
5980c0d06caSMauro Carvalho Chehab 		sd->gpio[1] |= val;
5990c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,
6000c0d06caSMauro Carvalho Chehab 			~sd->gpio[1] << 8);
6010c0d06caSMauro Carvalho Chehab 	}
6020c0d06caSMauro Carvalho Chehab }
6030c0d06caSMauro Carvalho Chehab 
gpio_init(struct sd * sd,const u8 * gpio)6040c0d06caSMauro Carvalho Chehab static void gpio_init(struct sd *sd,
6050c0d06caSMauro Carvalho Chehab 			const u8 *gpio)
6060c0d06caSMauro Carvalho Chehab {
6070c0d06caSMauro Carvalho Chehab 	gpio_set(sd, *gpio++, 0x000f);
6080c0d06caSMauro Carvalho Chehab 	gpio_set(sd, *gpio++, 0x000f);
6090c0d06caSMauro Carvalho Chehab 	gpio_set(sd, *gpio++, 0x000f);
6100c0d06caSMauro Carvalho Chehab 	gpio_set(sd, *gpio++, 0x000f);
6110c0d06caSMauro Carvalho Chehab 	gpio_set(sd, *gpio, 0x000f);
6120c0d06caSMauro Carvalho Chehab }
6130c0d06caSMauro Carvalho Chehab 
bridge_init(struct sd * sd)6140c0d06caSMauro Carvalho Chehab static void bridge_init(struct sd *sd)
6150c0d06caSMauro Carvalho Chehab {
6160c0d06caSMauro Carvalho Chehab 	static const struct ucbus_write_cmd clkfreq_cmd = {
6170c0d06caSMauro Carvalho Chehab 				0xf031, 0	/* SQ930_CLKFREQ_60MHZ */
6180c0d06caSMauro Carvalho Chehab 	};
6190c0d06caSMauro Carvalho Chehab 
6200c0d06caSMauro Carvalho Chehab 	ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);
6210c0d06caSMauro Carvalho Chehab 
6220c0d06caSMauro Carvalho Chehab 	gpio_set(sd, SQ930_GPIO_POWER, 0xff00);
6230c0d06caSMauro Carvalho Chehab }
6240c0d06caSMauro Carvalho Chehab 
cmos_probe(struct gspca_dev * gspca_dev)6250c0d06caSMauro Carvalho Chehab static void cmos_probe(struct gspca_dev *gspca_dev)
6260c0d06caSMauro Carvalho Chehab {
6270c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
6280c0d06caSMauro Carvalho Chehab 	int i;
6290c0d06caSMauro Carvalho Chehab 	const struct sensor_s *sensor;
6300c0d06caSMauro Carvalho Chehab 	static const u8 probe_order[] = {
6310c0d06caSMauro Carvalho Chehab /*		SENSOR_LZ24BP,		(tested as ccd) */
6320c0d06caSMauro Carvalho Chehab 		SENSOR_OV9630,
6330c0d06caSMauro Carvalho Chehab 		SENSOR_MI0360,
6340c0d06caSMauro Carvalho Chehab 		SENSOR_OV7660,
6350c0d06caSMauro Carvalho Chehab 		SENSOR_MT9V111,
6360c0d06caSMauro Carvalho Chehab 	};
6370c0d06caSMauro Carvalho Chehab 
6380c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
6390c0d06caSMauro Carvalho Chehab 		sensor = &sensor_tb[probe_order[i]];
6400c0d06caSMauro Carvalho Chehab 		ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);
6410c0d06caSMauro Carvalho Chehab 		gpio_init(sd, sensor->gpio);
6420c0d06caSMauro Carvalho Chehab 		msleep(100);
6430c0d06caSMauro Carvalho Chehab 		reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);
6440c0d06caSMauro Carvalho Chehab 		msleep(100);
6450c0d06caSMauro Carvalho Chehab 		if (gspca_dev->usb_buf[0] != 0)
6460c0d06caSMauro Carvalho Chehab 			break;
6470c0d06caSMauro Carvalho Chehab 	}
6480c0d06caSMauro Carvalho Chehab 	if (i >= ARRAY_SIZE(probe_order)) {
6490c0d06caSMauro Carvalho Chehab 		pr_err("Unknown sensor\n");
6500c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = -EINVAL;
6510c0d06caSMauro Carvalho Chehab 		return;
6520c0d06caSMauro Carvalho Chehab 	}
6530c0d06caSMauro Carvalho Chehab 	sd->sensor = probe_order[i];
6540c0d06caSMauro Carvalho Chehab 	switch (sd->sensor) {
6550c0d06caSMauro Carvalho Chehab 	case SENSOR_OV7660:
6560c0d06caSMauro Carvalho Chehab 	case SENSOR_OV9630:
6570c0d06caSMauro Carvalho Chehab 		pr_err("Sensor %s not yet treated\n",
6580c0d06caSMauro Carvalho Chehab 		       sensor_tb[sd->sensor].name);
6590c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = -EINVAL;
6600c0d06caSMauro Carvalho Chehab 		break;
6610c0d06caSMauro Carvalho Chehab 	}
6620c0d06caSMauro Carvalho Chehab }
6630c0d06caSMauro Carvalho Chehab 
mt9v111_init(struct gspca_dev * gspca_dev)6640c0d06caSMauro Carvalho Chehab static void mt9v111_init(struct gspca_dev *gspca_dev)
6650c0d06caSMauro Carvalho Chehab {
6660c0d06caSMauro Carvalho Chehab 	int i, nwait;
6670c0d06caSMauro Carvalho Chehab 	static const u8 cmd_001b[] = {
6680c0d06caSMauro Carvalho Chehab 		0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,
6690c0d06caSMauro Carvalho Chehab 		0x00, 0x00, 0x00
6700c0d06caSMauro Carvalho Chehab 	};
6710c0d06caSMauro Carvalho Chehab 	static const u8 cmd_011b[][7] = {
6720c0d06caSMauro Carvalho Chehab 		{0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},
6730c0d06caSMauro Carvalho Chehab 		{0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},
6740c0d06caSMauro Carvalho Chehab 		{0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},
6750c0d06caSMauro Carvalho Chehab 		{0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},
6760c0d06caSMauro Carvalho Chehab 	};
6770c0d06caSMauro Carvalho Chehab 
6780c0d06caSMauro Carvalho Chehab 	reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);
6790c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {
6800c0d06caSMauro Carvalho Chehab 		reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],
6810c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(cmd_011b[0]));
6820c0d06caSMauro Carvalho Chehab 		msleep(400);
6830c0d06caSMauro Carvalho Chehab 		nwait = 20;
6840c0d06caSMauro Carvalho Chehab 		for (;;) {
6850c0d06caSMauro Carvalho Chehab 			reg_r(gspca_dev, 0x031b, 1);
6860c0d06caSMauro Carvalho Chehab 			if (gspca_dev->usb_buf[0] == 0
6870c0d06caSMauro Carvalho Chehab 			 || gspca_dev->usb_err != 0)
6880c0d06caSMauro Carvalho Chehab 				break;
6890c0d06caSMauro Carvalho Chehab 			if (--nwait < 0) {
69037d5efb0SJoe Perches 				gspca_dbg(gspca_dev, D_PROBE, "mt9v111_init timeout\n");
6910c0d06caSMauro Carvalho Chehab 				gspca_dev->usb_err = -ETIME;
6920c0d06caSMauro Carvalho Chehab 				return;
6930c0d06caSMauro Carvalho Chehab 			}
6940c0d06caSMauro Carvalho Chehab 			msleep(50);
6950c0d06caSMauro Carvalho Chehab 		}
6960c0d06caSMauro Carvalho Chehab 	}
6970c0d06caSMauro Carvalho Chehab }
6980c0d06caSMauro Carvalho Chehab 
global_init(struct sd * sd,int first_time)6990c0d06caSMauro Carvalho Chehab static void global_init(struct sd *sd, int first_time)
7000c0d06caSMauro Carvalho Chehab {
7010c0d06caSMauro Carvalho Chehab 	switch (sd->sensor) {
7020c0d06caSMauro Carvalho Chehab 	case SENSOR_ICX098BQ:
7030c0d06caSMauro Carvalho Chehab 		if (first_time)
7040c0d06caSMauro Carvalho Chehab 			ucbus_write(&sd->gspca_dev,
7050c0d06caSMauro Carvalho Chehab 					icx098bq_start_0,
7060c0d06caSMauro Carvalho Chehab 					8, 8);
7070c0d06caSMauro Carvalho Chehab 		gpio_init(sd, sensor_tb[sd->sensor].gpio);
7080c0d06caSMauro Carvalho Chehab 		break;
7090c0d06caSMauro Carvalho Chehab 	case SENSOR_LZ24BP:
7100c0d06caSMauro Carvalho Chehab 		if (sd->type != Creative_live_motion)
7110c0d06caSMauro Carvalho Chehab 			gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);
7120c0d06caSMauro Carvalho Chehab 		else
7130c0d06caSMauro Carvalho Chehab 			gpio_set(sd, 0, 0x00ff);
7140c0d06caSMauro Carvalho Chehab 		msleep(50);
7150c0d06caSMauro Carvalho Chehab 		if (first_time)
7160c0d06caSMauro Carvalho Chehab 			ucbus_write(&sd->gspca_dev,
7170c0d06caSMauro Carvalho Chehab 					lz24bp_start_0,
7180c0d06caSMauro Carvalho Chehab 					8, 8);
7190c0d06caSMauro Carvalho Chehab 		gpio_init(sd, sensor_tb[sd->sensor].gpio);
7200c0d06caSMauro Carvalho Chehab 		break;
7210c0d06caSMauro Carvalho Chehab 	case SENSOR_MI0360:
7220c0d06caSMauro Carvalho Chehab 		if (first_time)
7230c0d06caSMauro Carvalho Chehab 			ucbus_write(&sd->gspca_dev,
7240c0d06caSMauro Carvalho Chehab 					mi0360_start_0,
7250c0d06caSMauro Carvalho Chehab 					ARRAY_SIZE(mi0360_start_0),
7260c0d06caSMauro Carvalho Chehab 					8);
7270c0d06caSMauro Carvalho Chehab 		gpio_init(sd, sensor_tb[sd->sensor].gpio);
7280c0d06caSMauro Carvalho Chehab 		gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);
7290c0d06caSMauro Carvalho Chehab 		break;
7300c0d06caSMauro Carvalho Chehab 	default:
7310c0d06caSMauro Carvalho Chehab /*	case SENSOR_MT9V111: */
7320c0d06caSMauro Carvalho Chehab 		if (first_time)
7330c0d06caSMauro Carvalho Chehab 			mt9v111_init(&sd->gspca_dev);
7340c0d06caSMauro Carvalho Chehab 		else
7350c0d06caSMauro Carvalho Chehab 			gpio_init(sd, sensor_tb[sd->sensor].gpio);
7360c0d06caSMauro Carvalho Chehab 		break;
7370c0d06caSMauro Carvalho Chehab 	}
7380c0d06caSMauro Carvalho Chehab }
7390c0d06caSMauro Carvalho Chehab 
lz24bp_ppl(struct sd * sd,u16 ppl)7400c0d06caSMauro Carvalho Chehab static void lz24bp_ppl(struct sd *sd, u16 ppl)
7410c0d06caSMauro Carvalho Chehab {
7420c0d06caSMauro Carvalho Chehab 	struct ucbus_write_cmd cmds[2] = {
7430c0d06caSMauro Carvalho Chehab 		{0xf810, ppl >> 8},
7440c0d06caSMauro Carvalho Chehab 		{0xf811, ppl}
7450c0d06caSMauro Carvalho Chehab 	};
7460c0d06caSMauro Carvalho Chehab 
7470c0d06caSMauro Carvalho Chehab 	ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
7480c0d06caSMauro Carvalho Chehab }
7490c0d06caSMauro Carvalho Chehab 
setexposure(struct gspca_dev * gspca_dev,s32 expo,s32 gain)7500c0d06caSMauro Carvalho Chehab static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
7510c0d06caSMauro Carvalho Chehab {
7520c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
7530c0d06caSMauro Carvalho Chehab 	int i, integclks, intstartclk, frameclks, min_frclk;
7540c0d06caSMauro Carvalho Chehab 	const struct sensor_s *sensor;
7550c0d06caSMauro Carvalho Chehab 	u16 cmd;
7560c0d06caSMauro Carvalho Chehab 	u8 buf[15];
7570c0d06caSMauro Carvalho Chehab 
7580c0d06caSMauro Carvalho Chehab 	integclks = expo;
7590c0d06caSMauro Carvalho Chehab 	i = 0;
7600c0d06caSMauro Carvalho Chehab 	cmd = SQ930_CTRL_SET_EXPOSURE;
7610c0d06caSMauro Carvalho Chehab 
7620c0d06caSMauro Carvalho Chehab 	switch (sd->sensor) {
7630c0d06caSMauro Carvalho Chehab 	case SENSOR_ICX098BQ:			/* ccd */
7640c0d06caSMauro Carvalho Chehab 	case SENSOR_LZ24BP:
7650c0d06caSMauro Carvalho Chehab 		min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;
7660c0d06caSMauro Carvalho Chehab 		if (integclks >= min_frclk) {
7670c0d06caSMauro Carvalho Chehab 			intstartclk = 0;
7680c0d06caSMauro Carvalho Chehab 			frameclks = integclks;
7690c0d06caSMauro Carvalho Chehab 		} else {
7700c0d06caSMauro Carvalho Chehab 			intstartclk = min_frclk - integclks;
7710c0d06caSMauro Carvalho Chehab 			frameclks = min_frclk;
7720c0d06caSMauro Carvalho Chehab 		}
7730c0d06caSMauro Carvalho Chehab 		buf[i++] = intstartclk >> 8;
7740c0d06caSMauro Carvalho Chehab 		buf[i++] = intstartclk;
7750c0d06caSMauro Carvalho Chehab 		buf[i++] = frameclks >> 8;
7760c0d06caSMauro Carvalho Chehab 		buf[i++] = frameclks;
7770c0d06caSMauro Carvalho Chehab 		buf[i++] = gain;
7780c0d06caSMauro Carvalho Chehab 		break;
7790c0d06caSMauro Carvalho Chehab 	default:				/* cmos */
7800c0d06caSMauro Carvalho Chehab /*	case SENSOR_MI0360: */
7810c0d06caSMauro Carvalho Chehab /*	case SENSOR_MT9V111: */
7820c0d06caSMauro Carvalho Chehab 		cmd |= 0x0100;
7830c0d06caSMauro Carvalho Chehab 		sensor = &sensor_tb[sd->sensor];
7840c0d06caSMauro Carvalho Chehab 		buf[i++] = sensor->i2c_addr;	/* i2c_slave_addr */
7850c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x08;	/* 2 * ni2c */
7860c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x09;	/* reg = shutter width */
7870c0d06caSMauro Carvalho Chehab 		buf[i++] = integclks >> 8; /* val H */
7880c0d06caSMauro Carvalho Chehab 		buf[i++] = sensor->i2c_dum;
7890c0d06caSMauro Carvalho Chehab 		buf[i++] = integclks;	/* val L */
7900c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x35;	/* reg = global gain */
7910c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x00;	/* val H */
7920c0d06caSMauro Carvalho Chehab 		buf[i++] = sensor->i2c_dum;
7930c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x80 + gain / 2; /* val L */
7940c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x00;
7950c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x00;
7960c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x00;
7970c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x00;
7980c0d06caSMauro Carvalho Chehab 		buf[i++] = 0x83;
7990c0d06caSMauro Carvalho Chehab 		break;
8000c0d06caSMauro Carvalho Chehab 	}
8010c0d06caSMauro Carvalho Chehab 	reg_wb(gspca_dev, cmd, 0, buf, i);
8020c0d06caSMauro Carvalho Chehab }
8030c0d06caSMauro Carvalho Chehab 
8040c0d06caSMauro Carvalho Chehab /* This function is called at probe time just before sd_init */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)8050c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev,
8060c0d06caSMauro Carvalho Chehab 		const struct usb_device_id *id)
8070c0d06caSMauro Carvalho Chehab {
8080c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
8090c0d06caSMauro Carvalho Chehab 	struct cam *cam = &gspca_dev->cam;
8100c0d06caSMauro Carvalho Chehab 
8110c0d06caSMauro Carvalho Chehab 	sd->sensor = id->driver_info >> 8;
8120c0d06caSMauro Carvalho Chehab 	sd->type = id->driver_info;
8130c0d06caSMauro Carvalho Chehab 
8140c0d06caSMauro Carvalho Chehab 	cam->cam_mode = vga_mode;
8150c0d06caSMauro Carvalho Chehab 	cam->nmodes = ARRAY_SIZE(vga_mode);
8160c0d06caSMauro Carvalho Chehab 
8170c0d06caSMauro Carvalho Chehab 	cam->bulk = 1;
8180c0d06caSMauro Carvalho Chehab 
8190c0d06caSMauro Carvalho Chehab 	return 0;
8200c0d06caSMauro Carvalho Chehab }
8210c0d06caSMauro Carvalho Chehab 
8220c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)8230c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev)
8240c0d06caSMauro Carvalho Chehab {
8250c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
8260c0d06caSMauro Carvalho Chehab 
8270c0d06caSMauro Carvalho Chehab 	sd->gpio[0] = sd->gpio[1] = 0xff;	/* force gpio rewrite */
8280c0d06caSMauro Carvalho Chehab 
8290c0d06caSMauro Carvalho Chehab /*fixme: is this needed for icx098bp and mi0360?
8300c0d06caSMauro Carvalho Chehab 	if (sd->sensor != SENSOR_LZ24BP)
8310c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);
8320c0d06caSMauro Carvalho Chehab  */
8330c0d06caSMauro Carvalho Chehab 
8340c0d06caSMauro Carvalho Chehab 	reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);
8350c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
8360c0d06caSMauro Carvalho Chehab 		return gspca_dev->usb_err;
8370c0d06caSMauro Carvalho Chehab 
8380c0d06caSMauro Carvalho Chehab /* it returns:
8390c0d06caSMauro Carvalho Chehab  * 03 00 12 93 0b f6 c9 00	live! ultra
8400c0d06caSMauro Carvalho Chehab  * 03 00 07 93 0b f6 ca 00	live! ultra for notebook
8410c0d06caSMauro Carvalho Chehab  * 03 00 12 93 0b fe c8 00	Trust WB-3500T
8420c0d06caSMauro Carvalho Chehab  * 02 00 06 93 0b fe c8 00	Joy-IT 318S
8430c0d06caSMauro Carvalho Chehab  * 03 00 12 93 0b f6 cf 00	icam tracer - sensor icx098bq
8440c0d06caSMauro Carvalho Chehab  * 02 00 12 93 0b fe cf 00	ProQ Motion Webcam
8450c0d06caSMauro Carvalho Chehab  *
8460c0d06caSMauro Carvalho Chehab  * byte
8470c0d06caSMauro Carvalho Chehab  * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)
8480c0d06caSMauro Carvalho Chehab  * 1: 00
8490c0d06caSMauro Carvalho Chehab  * 2: 06 / 07 / 12 = mode webcam? firmware??
8500c0d06caSMauro Carvalho Chehab  * 3: 93 chip = 930b (930b or 930c)
8510c0d06caSMauro Carvalho Chehab  * 4: 0b
8520c0d06caSMauro Carvalho Chehab  * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)
8530c0d06caSMauro Carvalho Chehab  * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?
8540c0d06caSMauro Carvalho Chehab  * 7: 00
8550c0d06caSMauro Carvalho Chehab  */
85637d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_PROBE, "info: %*ph\n", 8, gspca_dev->usb_buf);
8570c0d06caSMauro Carvalho Chehab 
8580c0d06caSMauro Carvalho Chehab 	bridge_init(sd);
8590c0d06caSMauro Carvalho Chehab 
8600c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_MI0360) {
8610c0d06caSMauro Carvalho Chehab 
8620c0d06caSMauro Carvalho Chehab 		/* no sensor probe for icam tracer */
8630c0d06caSMauro Carvalho Chehab 		if (gspca_dev->usb_buf[5] == 0xf6)	/* if ccd */
8640c0d06caSMauro Carvalho Chehab 			sd->sensor = SENSOR_ICX098BQ;
8650c0d06caSMauro Carvalho Chehab 		else
8660c0d06caSMauro Carvalho Chehab 			cmos_probe(gspca_dev);
8670c0d06caSMauro Carvalho Chehab 	}
8680c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err >= 0) {
86937d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PROBE, "Sensor %s\n",
87037d5efb0SJoe Perches 			  sensor_tb[sd->sensor].name);
8710c0d06caSMauro Carvalho Chehab 		global_init(sd, 1);
8720c0d06caSMauro Carvalho Chehab 	}
8730c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
8740c0d06caSMauro Carvalho Chehab }
8750c0d06caSMauro Carvalho Chehab 
8760c0d06caSMauro Carvalho Chehab /* send the start/stop commands to the webcam */
send_start(struct gspca_dev * gspca_dev)8770c0d06caSMauro Carvalho Chehab static void send_start(struct gspca_dev *gspca_dev)
8780c0d06caSMauro Carvalho Chehab {
8790c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
8800c0d06caSMauro Carvalho Chehab 	const struct cap_s *cap;
8810c0d06caSMauro Carvalho Chehab 	int mode;
8820c0d06caSMauro Carvalho Chehab 
8830c0d06caSMauro Carvalho Chehab 	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
8840c0d06caSMauro Carvalho Chehab 	cap = &capconfig[sd->sensor][mode];
8850c0d06caSMauro Carvalho Chehab 	reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
8860c0d06caSMauro Carvalho Chehab 			0x0a00 | cap->cc_sizeid,
8870c0d06caSMauro Carvalho Chehab 			cap->cc_bytes, 32);
8880c0d06caSMauro Carvalho Chehab }
8890c0d06caSMauro Carvalho Chehab 
send_stop(struct gspca_dev * gspca_dev)8900c0d06caSMauro Carvalho Chehab static void send_stop(struct gspca_dev *gspca_dev)
8910c0d06caSMauro Carvalho Chehab {
8920c0d06caSMauro Carvalho Chehab 	reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
8930c0d06caSMauro Carvalho Chehab }
8940c0d06caSMauro Carvalho Chehab 
8950c0d06caSMauro Carvalho Chehab /* function called at start time before URB creation */
sd_isoc_init(struct gspca_dev * gspca_dev)8960c0d06caSMauro Carvalho Chehab static int sd_isoc_init(struct gspca_dev *gspca_dev)
8970c0d06caSMauro Carvalho Chehab {
8980c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
8990c0d06caSMauro Carvalho Chehab 
9000c0d06caSMauro Carvalho Chehab 	gspca_dev->cam.bulk_nurbs = 1;	/* there must be one URB only */
9010c0d06caSMauro Carvalho Chehab 	sd->do_ctrl = 0;
9021966bc2aSOndrej Zary 	gspca_dev->cam.bulk_size = gspca_dev->pixfmt.width *
9031966bc2aSOndrej Zary 			gspca_dev->pixfmt.height + 8;
9040c0d06caSMauro Carvalho Chehab 	return 0;
9050c0d06caSMauro Carvalho Chehab }
9060c0d06caSMauro Carvalho Chehab 
9070c0d06caSMauro Carvalho Chehab /* start the capture */
sd_start(struct gspca_dev * gspca_dev)9080c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev)
9090c0d06caSMauro Carvalho Chehab {
9100c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
9110c0d06caSMauro Carvalho Chehab 	int mode;
9120c0d06caSMauro Carvalho Chehab 
9130c0d06caSMauro Carvalho Chehab 	bridge_init(sd);
9140c0d06caSMauro Carvalho Chehab 	global_init(sd, 0);
9150c0d06caSMauro Carvalho Chehab 	msleep(100);
9160c0d06caSMauro Carvalho Chehab 
9170c0d06caSMauro Carvalho Chehab 	switch (sd->sensor) {
9180c0d06caSMauro Carvalho Chehab 	case SENSOR_ICX098BQ:
9190c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, icx098bq_start_0,
9200c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(icx098bq_start_0),
9210c0d06caSMauro Carvalho Chehab 				8);
9220c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, icx098bq_start_1,
9230c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(icx098bq_start_1),
9240c0d06caSMauro Carvalho Chehab 				5);
9250c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, icx098bq_start_2,
9260c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(icx098bq_start_2),
9270c0d06caSMauro Carvalho Chehab 				6);
9280c0d06caSMauro Carvalho Chehab 		msleep(50);
9290c0d06caSMauro Carvalho Chehab 
9300c0d06caSMauro Carvalho Chehab 		/* 1st start */
9310c0d06caSMauro Carvalho Chehab 		send_start(gspca_dev);
9320c0d06caSMauro Carvalho Chehab 		gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
9330c0d06caSMauro Carvalho Chehab 		msleep(70);
9340c0d06caSMauro Carvalho Chehab 		reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
9350c0d06caSMauro Carvalho Chehab 		gpio_set(sd, 0x7f, 0x00ff);
9360c0d06caSMauro Carvalho Chehab 
9370c0d06caSMauro Carvalho Chehab 		/* 2nd start */
9380c0d06caSMauro Carvalho Chehab 		send_start(gspca_dev);
9390c0d06caSMauro Carvalho Chehab 		gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
9400c0d06caSMauro Carvalho Chehab 		goto out;
9410c0d06caSMauro Carvalho Chehab 	case SENSOR_LZ24BP:
9420c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, lz24bp_start_0,
9430c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(lz24bp_start_0),
9440c0d06caSMauro Carvalho Chehab 				8);
9450c0d06caSMauro Carvalho Chehab 		if (sd->type != Creative_live_motion)
9460c0d06caSMauro Carvalho Chehab 			ucbus_write(gspca_dev, lz24bp_start_1_gen,
9470c0d06caSMauro Carvalho Chehab 					ARRAY_SIZE(lz24bp_start_1_gen),
9480c0d06caSMauro Carvalho Chehab 					5);
9490c0d06caSMauro Carvalho Chehab 		else
9500c0d06caSMauro Carvalho Chehab 			ucbus_write(gspca_dev, lz24bp_start_1_clm,
9510c0d06caSMauro Carvalho Chehab 					ARRAY_SIZE(lz24bp_start_1_clm),
9520c0d06caSMauro Carvalho Chehab 					5);
9530c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, lz24bp_start_2,
9540c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(lz24bp_start_2),
9550c0d06caSMauro Carvalho Chehab 				6);
9560c0d06caSMauro Carvalho Chehab 		mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
9570c0d06caSMauro Carvalho Chehab 		lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
9580c0d06caSMauro Carvalho Chehab 		msleep(10);
9590c0d06caSMauro Carvalho Chehab 		break;
9600c0d06caSMauro Carvalho Chehab 	case SENSOR_MI0360:
9610c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, mi0360_start_0,
9620c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_start_0),
9630c0d06caSMauro Carvalho Chehab 				8);
9640c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mi0360_init_23,
9650c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_init_23));
9660c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mi0360_init_24,
9670c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_init_24));
9680c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mi0360_init_25,
9690c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_init_25));
9700c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, mi0360_start_1,
9710c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_start_1),
9720c0d06caSMauro Carvalho Chehab 				5);
9730c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mi0360_start_2,
9740c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_start_2));
9750c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mi0360_start_3,
9760c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_start_3));
9770c0d06caSMauro Carvalho Chehab 
9780c0d06caSMauro Carvalho Chehab 		/* 1st start */
9790c0d06caSMauro Carvalho Chehab 		send_start(gspca_dev);
9800c0d06caSMauro Carvalho Chehab 		msleep(60);
9810c0d06caSMauro Carvalho Chehab 		send_stop(gspca_dev);
9820c0d06caSMauro Carvalho Chehab 
9830c0d06caSMauro Carvalho Chehab 		i2c_write(sd,
9840c0d06caSMauro Carvalho Chehab 			mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
9850c0d06caSMauro Carvalho Chehab 		break;
9860c0d06caSMauro Carvalho Chehab 	default:
9870c0d06caSMauro Carvalho Chehab /*	case SENSOR_MT9V111: */
9880c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, mi0360_start_0,
9890c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mi0360_start_0),
9900c0d06caSMauro Carvalho Chehab 				8);
9910c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mt9v111_init_0,
9920c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mt9v111_init_0));
9930c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mt9v111_init_1,
9940c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mt9v111_init_1));
9950c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mt9v111_init_2,
9960c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mt9v111_init_2));
9970c0d06caSMauro Carvalho Chehab 		ucbus_write(gspca_dev, mt9v111_start_1,
9980c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mt9v111_start_1),
9990c0d06caSMauro Carvalho Chehab 				5);
10000c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mt9v111_init_3,
10010c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mt9v111_init_3));
10020c0d06caSMauro Carvalho Chehab 		i2c_write(sd, mt9v111_init_4,
10030c0d06caSMauro Carvalho Chehab 				ARRAY_SIZE(mt9v111_init_4));
10040c0d06caSMauro Carvalho Chehab 		break;
10050c0d06caSMauro Carvalho Chehab 	}
10060c0d06caSMauro Carvalho Chehab 
10070c0d06caSMauro Carvalho Chehab 	send_start(gspca_dev);
10080c0d06caSMauro Carvalho Chehab out:
10090c0d06caSMauro Carvalho Chehab 	msleep(1000);
10100c0d06caSMauro Carvalho Chehab 
10110c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_MT9V111)
10120c0d06caSMauro Carvalho Chehab 		gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
10130c0d06caSMauro Carvalho Chehab 
10140c0d06caSMauro Carvalho Chehab 	sd->do_ctrl = 1;	/* set the exposure */
10150c0d06caSMauro Carvalho Chehab 
10160c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
10170c0d06caSMauro Carvalho Chehab }
10180c0d06caSMauro Carvalho Chehab 
sd_stopN(struct gspca_dev * gspca_dev)10190c0d06caSMauro Carvalho Chehab static void sd_stopN(struct gspca_dev *gspca_dev)
10200c0d06caSMauro Carvalho Chehab {
10210c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
10220c0d06caSMauro Carvalho Chehab 
10230c0d06caSMauro Carvalho Chehab 	if (sd->sensor == SENSOR_MT9V111)
10240c0d06caSMauro Carvalho Chehab 		gpio_set(sd, 0, SQ930_GPIO_DFL_LED);
10250c0d06caSMauro Carvalho Chehab 	send_stop(gspca_dev);
10260c0d06caSMauro Carvalho Chehab }
10270c0d06caSMauro Carvalho Chehab 
10280c0d06caSMauro Carvalho Chehab /* function called when the application gets a new frame */
10290c0d06caSMauro Carvalho Chehab /* It sets the exposure if required and restart the bulk transfer. */
sd_dq_callback(struct gspca_dev * gspca_dev)10300c0d06caSMauro Carvalho Chehab static void sd_dq_callback(struct gspca_dev *gspca_dev)
10310c0d06caSMauro Carvalho Chehab {
10320c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
10330c0d06caSMauro Carvalho Chehab 	int ret;
10340c0d06caSMauro Carvalho Chehab 
10350c0d06caSMauro Carvalho Chehab 	if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)
10360c0d06caSMauro Carvalho Chehab 		return;
10370c0d06caSMauro Carvalho Chehab 	sd->do_ctrl = 0;
10380c0d06caSMauro Carvalho Chehab 
10390c0d06caSMauro Carvalho Chehab 	setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
10400c0d06caSMauro Carvalho Chehab 			v4l2_ctrl_g_ctrl(sd->gain));
10410c0d06caSMauro Carvalho Chehab 
10420c0d06caSMauro Carvalho Chehab 	gspca_dev->cam.bulk_nurbs = 1;
10432b89b73aSSebastian Andrzej Siewior 	ret = usb_submit_urb(gspca_dev->urb[0], GFP_KERNEL);
10440c0d06caSMauro Carvalho Chehab 	if (ret < 0)
10450c0d06caSMauro Carvalho Chehab 		pr_err("sd_dq_callback() err %d\n", ret);
10460c0d06caSMauro Carvalho Chehab 
10470c0d06caSMauro Carvalho Chehab 	/* wait a little time, otherwise the webcam crashes */
10480c0d06caSMauro Carvalho Chehab 	msleep(100);
10490c0d06caSMauro Carvalho Chehab }
10500c0d06caSMauro Carvalho Chehab 
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)10510c0d06caSMauro Carvalho Chehab static void sd_pkt_scan(struct gspca_dev *gspca_dev,
10520c0d06caSMauro Carvalho Chehab 			u8 *data,		/* isoc packet */
10530c0d06caSMauro Carvalho Chehab 			int len)		/* iso packet length */
10540c0d06caSMauro Carvalho Chehab {
10550c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
10560c0d06caSMauro Carvalho Chehab 
10570c0d06caSMauro Carvalho Chehab 	if (sd->do_ctrl)
10580c0d06caSMauro Carvalho Chehab 		gspca_dev->cam.bulk_nurbs = 0;
10590c0d06caSMauro Carvalho Chehab 	gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
10600c0d06caSMauro Carvalho Chehab 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
10610c0d06caSMauro Carvalho Chehab 	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
10620c0d06caSMauro Carvalho Chehab }
10630c0d06caSMauro Carvalho Chehab 
sd_s_ctrl(struct v4l2_ctrl * ctrl)10640c0d06caSMauro Carvalho Chehab static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
10650c0d06caSMauro Carvalho Chehab {
10660c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev =
10670c0d06caSMauro Carvalho Chehab 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
10680c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
10690c0d06caSMauro Carvalho Chehab 
10700c0d06caSMauro Carvalho Chehab 	gspca_dev->usb_err = 0;
10710c0d06caSMauro Carvalho Chehab 
10720c0d06caSMauro Carvalho Chehab 	if (!gspca_dev->streaming)
10730c0d06caSMauro Carvalho Chehab 		return 0;
10740c0d06caSMauro Carvalho Chehab 
10750c0d06caSMauro Carvalho Chehab 	switch (ctrl->id) {
10760c0d06caSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
10770c0d06caSMauro Carvalho Chehab 		setexposure(gspca_dev, ctrl->val, sd->gain->val);
10780c0d06caSMauro Carvalho Chehab 		break;
10790c0d06caSMauro Carvalho Chehab 	}
10800c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
10810c0d06caSMauro Carvalho Chehab }
10820c0d06caSMauro Carvalho Chehab 
10830c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops sd_ctrl_ops = {
10840c0d06caSMauro Carvalho Chehab 	.s_ctrl = sd_s_ctrl,
10850c0d06caSMauro Carvalho Chehab };
10860c0d06caSMauro Carvalho Chehab 
sd_init_controls(struct gspca_dev * gspca_dev)10870c0d06caSMauro Carvalho Chehab static int sd_init_controls(struct gspca_dev *gspca_dev)
10880c0d06caSMauro Carvalho Chehab {
10890c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
10900c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
10910c0d06caSMauro Carvalho Chehab 
10920c0d06caSMauro Carvalho Chehab 	gspca_dev->vdev.ctrl_handler = hdl;
10930c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(hdl, 2);
10940c0d06caSMauro Carvalho Chehab 	sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
10950c0d06caSMauro Carvalho Chehab 			V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
10960c0d06caSMauro Carvalho Chehab 	sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
10970c0d06caSMauro Carvalho Chehab 			V4L2_CID_GAIN, 1, 255, 1, 0x8d);
10980c0d06caSMauro Carvalho Chehab 
10990c0d06caSMauro Carvalho Chehab 	if (hdl->error) {
11000c0d06caSMauro Carvalho Chehab 		pr_err("Could not initialize controls\n");
11010c0d06caSMauro Carvalho Chehab 		return hdl->error;
11020c0d06caSMauro Carvalho Chehab 	}
11030c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_cluster(2, &sd->exposure);
11040c0d06caSMauro Carvalho Chehab 	return 0;
11050c0d06caSMauro Carvalho Chehab }
11060c0d06caSMauro Carvalho Chehab 
11070c0d06caSMauro Carvalho Chehab /* sub-driver description */
11080c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc = {
11090c0d06caSMauro Carvalho Chehab 	.name   = MODULE_NAME,
11100c0d06caSMauro Carvalho Chehab 	.config = sd_config,
11110c0d06caSMauro Carvalho Chehab 	.init   = sd_init,
11120c0d06caSMauro Carvalho Chehab 	.init_controls = sd_init_controls,
11130c0d06caSMauro Carvalho Chehab 	.isoc_init = sd_isoc_init,
11140c0d06caSMauro Carvalho Chehab 	.start  = sd_start,
11150c0d06caSMauro Carvalho Chehab 	.stopN  = sd_stopN,
11160c0d06caSMauro Carvalho Chehab 	.pkt_scan = sd_pkt_scan,
11170c0d06caSMauro Carvalho Chehab 	.dq_callback = sd_dq_callback,
11180c0d06caSMauro Carvalho Chehab };
11190c0d06caSMauro Carvalho Chehab 
11200c0d06caSMauro Carvalho Chehab /* Table of supported USB devices */
11210c0d06caSMauro Carvalho Chehab #define ST(sensor, type) \
11220c0d06caSMauro Carvalho Chehab 	.driver_info = (SENSOR_ ## sensor << 8) \
11230c0d06caSMauro Carvalho Chehab 			| (type)
11240c0d06caSMauro Carvalho Chehab static const struct usb_device_id device_table[] = {
11250c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},
11260c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},
11270c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},
11280c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},
11290c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},
11300c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},
11310c0d06caSMauro Carvalho Chehab 	{}
11320c0d06caSMauro Carvalho Chehab };
11330c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table);
11340c0d06caSMauro Carvalho Chehab 
11350c0d06caSMauro Carvalho Chehab 
11360c0d06caSMauro Carvalho Chehab /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)11370c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf,
11380c0d06caSMauro Carvalho Chehab 		const struct usb_device_id *id)
11390c0d06caSMauro Carvalho Chehab {
11400c0d06caSMauro Carvalho Chehab 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
11410c0d06caSMauro Carvalho Chehab 			THIS_MODULE);
11420c0d06caSMauro Carvalho Chehab }
11430c0d06caSMauro Carvalho Chehab 
11440c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = {
11450c0d06caSMauro Carvalho Chehab 	.name	    = MODULE_NAME,
11460c0d06caSMauro Carvalho Chehab 	.id_table   = device_table,
11470c0d06caSMauro Carvalho Chehab 	.probe	    = sd_probe,
11480c0d06caSMauro Carvalho Chehab 	.disconnect = gspca_disconnect,
11490c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
11500c0d06caSMauro Carvalho Chehab 	.suspend    = gspca_suspend,
11510c0d06caSMauro Carvalho Chehab 	.resume     = gspca_resume,
11520c0d06caSMauro Carvalho Chehab 	.reset_resume = gspca_resume,
11530c0d06caSMauro Carvalho Chehab #endif
11540c0d06caSMauro Carvalho Chehab };
11550c0d06caSMauro Carvalho Chehab 
11560c0d06caSMauro Carvalho Chehab module_usb_driver(sd_driver);
1157