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