162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SQ930x subdriver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>
662306a36Sopenharmony_ci * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>
762306a36Sopenharmony_ci * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define MODULE_NAME "sq930x"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "gspca.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciMODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
1762306a36Sopenharmony_ci		"Gerard Klaver <gerard at gkall dot hobby dot nl\n"
1862306a36Sopenharmony_ci		"Sam Revitch <samr7@cs.washington.edu>");
1962306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
2062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Structure to hold all of our device specific stuff */
2362306a36Sopenharmony_cistruct sd {
2462306a36Sopenharmony_ci	struct gspca_dev gspca_dev;	/* !! must be the first item */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	struct { /* exposure/gain control cluster */
2762306a36Sopenharmony_ci		struct v4l2_ctrl *exposure;
2862306a36Sopenharmony_ci		struct v4l2_ctrl *gain;
2962306a36Sopenharmony_ci	};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	u8 do_ctrl;
3262306a36Sopenharmony_ci	u8 gpio[2];
3362306a36Sopenharmony_ci	u8 sensor;
3462306a36Sopenharmony_ci	u8 type;
3562306a36Sopenharmony_ci#define Generic 0
3662306a36Sopenharmony_ci#define Creative_live_motion 1
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_cienum sensors {
3962306a36Sopenharmony_ci	SENSOR_ICX098BQ,
4062306a36Sopenharmony_ci	SENSOR_LZ24BP,
4162306a36Sopenharmony_ci	SENSOR_MI0360,
4262306a36Sopenharmony_ci	SENSOR_MT9V111,		/* = MI360SOC */
4362306a36Sopenharmony_ci	SENSOR_OV7660,
4462306a36Sopenharmony_ci	SENSOR_OV9630,
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic struct v4l2_pix_format vga_mode[] = {
4862306a36Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
4962306a36Sopenharmony_ci		.bytesperline = 320,
5062306a36Sopenharmony_ci		.sizeimage = 320 * 240,
5162306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
5262306a36Sopenharmony_ci		.priv = 0},
5362306a36Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
5462306a36Sopenharmony_ci		.bytesperline = 640,
5562306a36Sopenharmony_ci		.sizeimage = 640 * 480,
5662306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
5762306a36Sopenharmony_ci		.priv = 1},
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* sq930x registers */
6162306a36Sopenharmony_ci#define SQ930_CTRL_UCBUS_IO	0x0001
6262306a36Sopenharmony_ci#define SQ930_CTRL_I2C_IO	0x0002
6362306a36Sopenharmony_ci#define SQ930_CTRL_GPIO		0x0005
6462306a36Sopenharmony_ci#define SQ930_CTRL_CAP_START	0x0010
6562306a36Sopenharmony_ci#define SQ930_CTRL_CAP_STOP	0x0011
6662306a36Sopenharmony_ci#define SQ930_CTRL_SET_EXPOSURE 0x001d
6762306a36Sopenharmony_ci#define SQ930_CTRL_RESET	0x001e
6862306a36Sopenharmony_ci#define SQ930_CTRL_GET_DEV_INFO 0x001f
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* gpio 1 (8..15) */
7162306a36Sopenharmony_ci#define SQ930_GPIO_DFL_I2C_SDA	0x0001
7262306a36Sopenharmony_ci#define SQ930_GPIO_DFL_I2C_SCL	0x0002
7362306a36Sopenharmony_ci#define SQ930_GPIO_RSTBAR	0x0004
7462306a36Sopenharmony_ci#define SQ930_GPIO_EXTRA1	0x0040
7562306a36Sopenharmony_ci#define SQ930_GPIO_EXTRA2	0x0080
7662306a36Sopenharmony_ci/* gpio 3 (24..31) */
7762306a36Sopenharmony_ci#define SQ930_GPIO_POWER	0x0200
7862306a36Sopenharmony_ci#define SQ930_GPIO_DFL_LED	0x1000
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct ucbus_write_cmd {
8162306a36Sopenharmony_ci	u16	bw_addr;
8262306a36Sopenharmony_ci	u8	bw_data;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_cistruct i2c_write_cmd {
8562306a36Sopenharmony_ci	u8	reg;
8662306a36Sopenharmony_ci	u16	val;
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const struct ucbus_write_cmd icx098bq_start_0[] = {
9062306a36Sopenharmony_ci	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},
9162306a36Sopenharmony_ci	{0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},
9262306a36Sopenharmony_ci	{0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},
9362306a36Sopenharmony_ci	{0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},
9462306a36Sopenharmony_ci	{0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},
9562306a36Sopenharmony_ci	{0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},
9662306a36Sopenharmony_ci	{0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},
9762306a36Sopenharmony_ci	{0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},
9862306a36Sopenharmony_ci	{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
9962306a36Sopenharmony_ci	{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
10062306a36Sopenharmony_ci	{0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},
10162306a36Sopenharmony_ci	{0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
10262306a36Sopenharmony_ci	{0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},
10362306a36Sopenharmony_ci	{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
10462306a36Sopenharmony_ci	{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
10562306a36Sopenharmony_ci	{0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},
10662306a36Sopenharmony_ci	{0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},
10762306a36Sopenharmony_ci	{0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},
10862306a36Sopenharmony_ci	{0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},
10962306a36Sopenharmony_ci	{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
11062306a36Sopenharmony_ci	{0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},
11162306a36Sopenharmony_ci	{0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},
11262306a36Sopenharmony_ci	{0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},
11362306a36Sopenharmony_ci	{0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},
11462306a36Sopenharmony_ci	{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
11562306a36Sopenharmony_ci	{0xf800, 0x03}
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_cistatic const struct ucbus_write_cmd icx098bq_start_1[] = {
11862306a36Sopenharmony_ci	{0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
11962306a36Sopenharmony_ci	{0xf5f4, 0xc0},
12062306a36Sopenharmony_ci	{0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
12162306a36Sopenharmony_ci	{0xf5f4, 0xc0},
12262306a36Sopenharmony_ci	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
12362306a36Sopenharmony_ci	{0xf5f9, 0x00}
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic const struct ucbus_write_cmd icx098bq_start_2[] = {
12762306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},
12862306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03},
12962306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},
13062306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03},
13162306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},
13262306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03},
13362306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
13462306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03}
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic const struct ucbus_write_cmd lz24bp_start_0[] = {
13862306a36Sopenharmony_ci	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},
13962306a36Sopenharmony_ci	{0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},
14062306a36Sopenharmony_ci	{0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},
14162306a36Sopenharmony_ci	{0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},
14262306a36Sopenharmony_ci	{0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},
14362306a36Sopenharmony_ci	{0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},
14462306a36Sopenharmony_ci	{0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},
14562306a36Sopenharmony_ci	{0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},
14662306a36Sopenharmony_ci	{0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
14762306a36Sopenharmony_ci	{0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
14862306a36Sopenharmony_ci	{0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},
14962306a36Sopenharmony_ci	{0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
15062306a36Sopenharmony_ci	{0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},
15162306a36Sopenharmony_ci	{0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
15262306a36Sopenharmony_ci	{0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
15362306a36Sopenharmony_ci	{0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},
15462306a36Sopenharmony_ci	{0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},
15562306a36Sopenharmony_ci	{0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},
15662306a36Sopenharmony_ci	{0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},
15762306a36Sopenharmony_ci	{0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
15862306a36Sopenharmony_ci	{0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},
15962306a36Sopenharmony_ci	{0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},
16062306a36Sopenharmony_ci	{0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},
16162306a36Sopenharmony_ci	{0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},
16262306a36Sopenharmony_ci	{0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
16362306a36Sopenharmony_ci	{0xf800, 0x03}
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_cistatic const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
16662306a36Sopenharmony_ci	{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
16762306a36Sopenharmony_ci	{0xf5f4, 0xb3},
16862306a36Sopenharmony_ci	{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
16962306a36Sopenharmony_ci	{0xf5f4, 0xb3},
17062306a36Sopenharmony_ci	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
17162306a36Sopenharmony_ci	{0xf5f9, 0x00}
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
17562306a36Sopenharmony_ci	{0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
17662306a36Sopenharmony_ci	{0xf5f4, 0xc0},
17762306a36Sopenharmony_ci	{0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
17862306a36Sopenharmony_ci	{0xf5f4, 0xc0},
17962306a36Sopenharmony_ci	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
18062306a36Sopenharmony_ci	{0xf5f9, 0x00}
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic const struct ucbus_write_cmd lz24bp_start_2[] = {
18462306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},
18562306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03},
18662306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},
18762306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03},
18862306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},
18962306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03},
19062306a36Sopenharmony_ci	{0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
19162306a36Sopenharmony_ci	{0xf807, 0x7f}, {0xf800, 0x03}
19262306a36Sopenharmony_ci};
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic const struct ucbus_write_cmd mi0360_start_0[] = {
19562306a36Sopenharmony_ci	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},
19662306a36Sopenharmony_ci	{0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_cistatic const struct i2c_write_cmd mi0360_init_23[] = {
19962306a36Sopenharmony_ci	{0x30, 0x0040},		/* reserved - def 0x0005 */
20062306a36Sopenharmony_ci	{0x31, 0x0000},		/* reserved - def 0x002a */
20162306a36Sopenharmony_ci	{0x34, 0x0100},		/* reserved - def 0x0100 */
20262306a36Sopenharmony_ci	{0x3d, 0x068f},		/* reserved - def 0x068f */
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_cistatic const struct i2c_write_cmd mi0360_init_24[] = {
20562306a36Sopenharmony_ci	{0x03, 0x01e5},		/* window height */
20662306a36Sopenharmony_ci	{0x04, 0x0285},		/* window width */
20762306a36Sopenharmony_ci};
20862306a36Sopenharmony_cistatic const struct i2c_write_cmd mi0360_init_25[] = {
20962306a36Sopenharmony_ci	{0x35, 0x0020},		/* global gain */
21062306a36Sopenharmony_ci	{0x2b, 0x0020},		/* green1 gain */
21162306a36Sopenharmony_ci	{0x2c, 0x002a},		/* blue gain */
21262306a36Sopenharmony_ci	{0x2d, 0x0028},		/* red gain */
21362306a36Sopenharmony_ci	{0x2e, 0x0020},		/* green2 gain */
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_cistatic const struct ucbus_write_cmd mi0360_start_1[] = {
21662306a36Sopenharmony_ci	{0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
21762306a36Sopenharmony_ci	{0xf5f4, 0xa6},
21862306a36Sopenharmony_ci	{0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
21962306a36Sopenharmony_ci	{0xf5f4, 0xa6},
22062306a36Sopenharmony_ci	{0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
22162306a36Sopenharmony_ci	{0xf5f9, 0x00}
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_cistatic const struct i2c_write_cmd mi0360_start_2[] = {
22462306a36Sopenharmony_ci	{0x62, 0x041d},		/* reserved - def 0x0418 */
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_cistatic const struct i2c_write_cmd mi0360_start_3[] = {
22762306a36Sopenharmony_ci	{0x05, 0x007b},		/* horiz blanking */
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_cistatic const struct i2c_write_cmd mi0360_start_4[] = {
23062306a36Sopenharmony_ci	{0x05, 0x03f5},		/* horiz blanking */
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic const struct i2c_write_cmd mt9v111_init_0[] = {
23462306a36Sopenharmony_ci	{0x01, 0x0001},		/* select IFP/SOC registers */
23562306a36Sopenharmony_ci	{0x06, 0x300c},		/* operating mode control */
23662306a36Sopenharmony_ci	{0x08, 0xcc00},		/* output format control (RGB) */
23762306a36Sopenharmony_ci	{0x01, 0x0004},		/* select sensor core registers */
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_cistatic const struct i2c_write_cmd mt9v111_init_1[] = {
24062306a36Sopenharmony_ci	{0x03, 0x01e5},		/* window height */
24162306a36Sopenharmony_ci	{0x04, 0x0285},		/* window width */
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_cistatic const struct i2c_write_cmd mt9v111_init_2[] = {
24462306a36Sopenharmony_ci	{0x30, 0x7800},
24562306a36Sopenharmony_ci	{0x31, 0x0000},
24662306a36Sopenharmony_ci	{0x07, 0x3002},		/* output control */
24762306a36Sopenharmony_ci	{0x35, 0x0020},		/* global gain */
24862306a36Sopenharmony_ci	{0x2b, 0x0020},		/* green1 gain */
24962306a36Sopenharmony_ci	{0x2c, 0x0020},		/* blue gain */
25062306a36Sopenharmony_ci	{0x2d, 0x0020},		/* red gain */
25162306a36Sopenharmony_ci	{0x2e, 0x0020},		/* green2 gain */
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_cistatic const struct ucbus_write_cmd mt9v111_start_1[] = {
25462306a36Sopenharmony_ci	{0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
25562306a36Sopenharmony_ci	{0xf5f4, 0xaa},
25662306a36Sopenharmony_ci	{0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
25762306a36Sopenharmony_ci	{0xf5f4, 0xaa},
25862306a36Sopenharmony_ci	{0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},
25962306a36Sopenharmony_ci	{0xf5f9, 0x0a}
26062306a36Sopenharmony_ci};
26162306a36Sopenharmony_cistatic const struct i2c_write_cmd mt9v111_init_3[] = {
26262306a36Sopenharmony_ci	{0x62, 0x0405},
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_cistatic const struct i2c_write_cmd mt9v111_init_4[] = {
26562306a36Sopenharmony_ci/*	{0x05, 0x00ce}, */
26662306a36Sopenharmony_ci	{0x05, 0x005d},		/* horizontal blanking */
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic const struct ucbus_write_cmd ov7660_start_0[] = {
27062306a36Sopenharmony_ci	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},
27162306a36Sopenharmony_ci	{0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic const struct ucbus_write_cmd ov9630_start_0[] = {
27562306a36Sopenharmony_ci	{0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},
27662306a36Sopenharmony_ci	{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/* start parameters indexed by [sensor][mode] */
28062306a36Sopenharmony_cistatic const struct cap_s {
28162306a36Sopenharmony_ci	u8	cc_sizeid;
28262306a36Sopenharmony_ci	u8	cc_bytes[32];
28362306a36Sopenharmony_ci} capconfig[4][2] = {
28462306a36Sopenharmony_ci	[SENSOR_ICX098BQ] = {
28562306a36Sopenharmony_ci		{2,				/* Bayer 320x240 */
28662306a36Sopenharmony_ci		  {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
28762306a36Sopenharmony_ci		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
28862306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
28962306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
29062306a36Sopenharmony_ci		{4,				/* Bayer 640x480 */
29162306a36Sopenharmony_ci		  {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
29262306a36Sopenharmony_ci		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
29362306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29462306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
29562306a36Sopenharmony_ci	},
29662306a36Sopenharmony_ci	[SENSOR_LZ24BP] = {
29762306a36Sopenharmony_ci		{2,				/* Bayer 320x240 */
29862306a36Sopenharmony_ci		  {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
29962306a36Sopenharmony_ci		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
30062306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30162306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
30262306a36Sopenharmony_ci		{4,				/* Bayer 640x480 */
30362306a36Sopenharmony_ci		  {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
30462306a36Sopenharmony_ci		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
30562306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30662306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
30762306a36Sopenharmony_ci	},
30862306a36Sopenharmony_ci	[SENSOR_MI0360] = {
30962306a36Sopenharmony_ci		{2,				/* Bayer 320x240 */
31062306a36Sopenharmony_ci		  {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
31162306a36Sopenharmony_ci		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
31262306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31362306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
31462306a36Sopenharmony_ci		{4,				/* Bayer 640x480 */
31562306a36Sopenharmony_ci		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
31662306a36Sopenharmony_ci		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
31762306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31862306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
31962306a36Sopenharmony_ci	},
32062306a36Sopenharmony_ci	[SENSOR_MT9V111] = {
32162306a36Sopenharmony_ci		{2,				/* Bayer 320x240 */
32262306a36Sopenharmony_ci		  {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
32362306a36Sopenharmony_ci		   0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
32462306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32562306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
32662306a36Sopenharmony_ci		{4,				/* Bayer 640x480 */
32762306a36Sopenharmony_ci		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
32862306a36Sopenharmony_ci		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
32962306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33062306a36Sopenharmony_ci		   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
33162306a36Sopenharmony_ci	},
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistruct sensor_s {
33562306a36Sopenharmony_ci	const char *name;
33662306a36Sopenharmony_ci	u8 i2c_addr;
33762306a36Sopenharmony_ci	u8 i2c_dum;
33862306a36Sopenharmony_ci	u8 gpio[5];
33962306a36Sopenharmony_ci	u8 cmd_len;
34062306a36Sopenharmony_ci	const struct ucbus_write_cmd *cmd;
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic const struct sensor_s sensor_tb[] = {
34462306a36Sopenharmony_ci	[SENSOR_ICX098BQ] = {
34562306a36Sopenharmony_ci		"icx098bp",
34662306a36Sopenharmony_ci		0x00, 0x00,
34762306a36Sopenharmony_ci		{0,
34862306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
34962306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA,
35062306a36Sopenharmony_ci		 0,
35162306a36Sopenharmony_ci		 SQ930_GPIO_RSTBAR
35262306a36Sopenharmony_ci		},
35362306a36Sopenharmony_ci		8, icx098bq_start_0
35462306a36Sopenharmony_ci	    },
35562306a36Sopenharmony_ci	[SENSOR_LZ24BP] = {
35662306a36Sopenharmony_ci		"lz24bp",
35762306a36Sopenharmony_ci		0x00, 0x00,
35862306a36Sopenharmony_ci		{0,
35962306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
36062306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA,
36162306a36Sopenharmony_ci		 0,
36262306a36Sopenharmony_ci		 SQ930_GPIO_RSTBAR
36362306a36Sopenharmony_ci		},
36462306a36Sopenharmony_ci		8, lz24bp_start_0
36562306a36Sopenharmony_ci	    },
36662306a36Sopenharmony_ci	[SENSOR_MI0360] = {
36762306a36Sopenharmony_ci		"mi0360",
36862306a36Sopenharmony_ci		0x5d, 0x80,
36962306a36Sopenharmony_ci		{SQ930_GPIO_RSTBAR,
37062306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
37162306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA,
37262306a36Sopenharmony_ci		 0,
37362306a36Sopenharmony_ci		 0
37462306a36Sopenharmony_ci		},
37562306a36Sopenharmony_ci		7, mi0360_start_0
37662306a36Sopenharmony_ci	    },
37762306a36Sopenharmony_ci	[SENSOR_MT9V111] = {
37862306a36Sopenharmony_ci		"mt9v111",
37962306a36Sopenharmony_ci		0x5c, 0x7f,
38062306a36Sopenharmony_ci		{SQ930_GPIO_RSTBAR,
38162306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
38262306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA,
38362306a36Sopenharmony_ci		 0,
38462306a36Sopenharmony_ci		 0
38562306a36Sopenharmony_ci		},
38662306a36Sopenharmony_ci		7, mi0360_start_0
38762306a36Sopenharmony_ci	    },
38862306a36Sopenharmony_ci	[SENSOR_OV7660] = {
38962306a36Sopenharmony_ci		"ov7660",
39062306a36Sopenharmony_ci		0x21, 0x00,
39162306a36Sopenharmony_ci		{0,
39262306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
39362306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA,
39462306a36Sopenharmony_ci		 0,
39562306a36Sopenharmony_ci		 SQ930_GPIO_RSTBAR
39662306a36Sopenharmony_ci		},
39762306a36Sopenharmony_ci		7, ov7660_start_0
39862306a36Sopenharmony_ci	    },
39962306a36Sopenharmony_ci	[SENSOR_OV9630] = {
40062306a36Sopenharmony_ci		"ov9630",
40162306a36Sopenharmony_ci		0x30, 0x00,
40262306a36Sopenharmony_ci		{0,
40362306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
40462306a36Sopenharmony_ci		 SQ930_GPIO_DFL_I2C_SDA,
40562306a36Sopenharmony_ci		 0,
40662306a36Sopenharmony_ci		 SQ930_GPIO_RSTBAR
40762306a36Sopenharmony_ci		},
40862306a36Sopenharmony_ci		7, ov9630_start_0
40962306a36Sopenharmony_ci	    },
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void reg_r(struct gspca_dev *gspca_dev,
41362306a36Sopenharmony_ci		u16 value, int len)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	int ret;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (gspca_dev->usb_err < 0)
41862306a36Sopenharmony_ci		return;
41962306a36Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
42062306a36Sopenharmony_ci			usb_rcvctrlpipe(gspca_dev->dev, 0),
42162306a36Sopenharmony_ci			0x0c,
42262306a36Sopenharmony_ci			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
42362306a36Sopenharmony_ci			value, 0, gspca_dev->usb_buf, len,
42462306a36Sopenharmony_ci			500);
42562306a36Sopenharmony_ci	if (ret < 0) {
42662306a36Sopenharmony_ci		pr_err("reg_r %04x failed %d\n", value, ret);
42762306a36Sopenharmony_ci		gspca_dev->usb_err = ret;
42862306a36Sopenharmony_ci		/*
42962306a36Sopenharmony_ci		 * Make sure the buffer is zeroed to avoid uninitialized
43062306a36Sopenharmony_ci		 * values.
43162306a36Sopenharmony_ci		 */
43262306a36Sopenharmony_ci		memset(gspca_dev->usb_buf, 0, USB_BUF_SZ);
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	int ret;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (gspca_dev->usb_err < 0)
44162306a36Sopenharmony_ci		return;
44262306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "reg_w v: %04x i: %04x\n", value, index);
44362306a36Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
44462306a36Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
44562306a36Sopenharmony_ci			0x0c,			/* request */
44662306a36Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
44762306a36Sopenharmony_ci			value, index, NULL, 0,
44862306a36Sopenharmony_ci			500);
44962306a36Sopenharmony_ci	msleep(30);
45062306a36Sopenharmony_ci	if (ret < 0) {
45162306a36Sopenharmony_ci		pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
45262306a36Sopenharmony_ci		gspca_dev->usb_err = ret;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
45762306a36Sopenharmony_ci		const u8 *data, int len)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	int ret;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (gspca_dev->usb_err < 0)
46262306a36Sopenharmony_ci		return;
46362306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "reg_wb v: %04x i: %04x %02x...%02x\n",
46462306a36Sopenharmony_ci		  value, index, *data, data[len - 1]);
46562306a36Sopenharmony_ci	memcpy(gspca_dev->usb_buf, data, len);
46662306a36Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
46762306a36Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
46862306a36Sopenharmony_ci			0x0c,			/* request */
46962306a36Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
47062306a36Sopenharmony_ci			value, index, gspca_dev->usb_buf, len,
47162306a36Sopenharmony_ci			1000);
47262306a36Sopenharmony_ci	msleep(30);
47362306a36Sopenharmony_ci	if (ret < 0) {
47462306a36Sopenharmony_ci		pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
47562306a36Sopenharmony_ci		gspca_dev->usb_err = ret;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void i2c_write(struct sd *sd,
48062306a36Sopenharmony_ci			const struct i2c_write_cmd *cmd,
48162306a36Sopenharmony_ci			int ncmds)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct gspca_dev *gspca_dev = &sd->gspca_dev;
48462306a36Sopenharmony_ci	const struct sensor_s *sensor;
48562306a36Sopenharmony_ci	u16 val, idx;
48662306a36Sopenharmony_ci	u8 *buf;
48762306a36Sopenharmony_ci	int ret;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (gspca_dev->usb_err < 0)
49062306a36Sopenharmony_ci		return;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	sensor = &sensor_tb[sd->sensor];
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;
49562306a36Sopenharmony_ci	idx = (cmd->val & 0xff00) | cmd->reg;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	buf = gspca_dev->usb_buf;
49862306a36Sopenharmony_ci	*buf++ = sensor->i2c_dum;
49962306a36Sopenharmony_ci	*buf++ = cmd->val;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	while (--ncmds > 0) {
50262306a36Sopenharmony_ci		cmd++;
50362306a36Sopenharmony_ci		*buf++ = cmd->reg;
50462306a36Sopenharmony_ci		*buf++ = cmd->val >> 8;
50562306a36Sopenharmony_ci		*buf++ = sensor->i2c_dum;
50662306a36Sopenharmony_ci		*buf++ = cmd->val;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "i2c_w v: %04x i: %04x %02x...%02x\n",
51062306a36Sopenharmony_ci		  val, idx, gspca_dev->usb_buf[0], buf[-1]);
51162306a36Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
51262306a36Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
51362306a36Sopenharmony_ci			0x0c,			/* request */
51462306a36Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
51562306a36Sopenharmony_ci			val, idx,
51662306a36Sopenharmony_ci			gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
51762306a36Sopenharmony_ci			500);
51862306a36Sopenharmony_ci	if (ret < 0) {
51962306a36Sopenharmony_ci		pr_err("i2c_write failed %d\n", ret);
52062306a36Sopenharmony_ci		gspca_dev->usb_err = ret;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic void ucbus_write(struct gspca_dev *gspca_dev,
52562306a36Sopenharmony_ci			const struct ucbus_write_cmd *cmd,
52662306a36Sopenharmony_ci			int ncmds,
52762306a36Sopenharmony_ci			int batchsize)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	u8 *buf;
53062306a36Sopenharmony_ci	u16 val, idx;
53162306a36Sopenharmony_ci	int len, ret;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (gspca_dev->usb_err < 0)
53462306a36Sopenharmony_ci		return;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if ((batchsize - 1) * 3 > USB_BUF_SZ) {
53762306a36Sopenharmony_ci		gspca_err(gspca_dev, "Bug: usb_buf overflow\n");
53862306a36Sopenharmony_ci		gspca_dev->usb_err = -ENOMEM;
53962306a36Sopenharmony_ci		return;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	for (;;) {
54362306a36Sopenharmony_ci		len = ncmds;
54462306a36Sopenharmony_ci		if (len > batchsize)
54562306a36Sopenharmony_ci			len = batchsize;
54662306a36Sopenharmony_ci		ncmds -= len;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;
54962306a36Sopenharmony_ci		idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		buf = gspca_dev->usb_buf;
55262306a36Sopenharmony_ci		while (--len > 0) {
55362306a36Sopenharmony_ci			cmd++;
55462306a36Sopenharmony_ci			*buf++ = cmd->bw_addr;
55562306a36Sopenharmony_ci			*buf++ = cmd->bw_addr >> 8;
55662306a36Sopenharmony_ci			*buf++ = cmd->bw_data;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci		if (buf != gspca_dev->usb_buf)
55962306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x %02x...%02x\n",
56062306a36Sopenharmony_ci				  val, idx,
56162306a36Sopenharmony_ci				  gspca_dev->usb_buf[0], buf[-1]);
56262306a36Sopenharmony_ci		else
56362306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_USBO, "ucbus v: %04x i: %04x\n",
56462306a36Sopenharmony_ci				  val, idx);
56562306a36Sopenharmony_ci		ret = usb_control_msg(gspca_dev->dev,
56662306a36Sopenharmony_ci				usb_sndctrlpipe(gspca_dev->dev, 0),
56762306a36Sopenharmony_ci				0x0c,			/* request */
56862306a36Sopenharmony_ci			   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
56962306a36Sopenharmony_ci				val, idx,
57062306a36Sopenharmony_ci				gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
57162306a36Sopenharmony_ci				500);
57262306a36Sopenharmony_ci		if (ret < 0) {
57362306a36Sopenharmony_ci			pr_err("ucbus_write failed %d\n", ret);
57462306a36Sopenharmony_ci			gspca_dev->usb_err = ret;
57562306a36Sopenharmony_ci			return;
57662306a36Sopenharmony_ci		}
57762306a36Sopenharmony_ci		msleep(30);
57862306a36Sopenharmony_ci		if (ncmds <= 0)
57962306a36Sopenharmony_ci			break;
58062306a36Sopenharmony_ci		cmd++;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void gpio_set(struct sd *sd, u16 val, u16 mask)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct gspca_dev *gspca_dev = &sd->gspca_dev;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (mask & 0x00ff) {
58962306a36Sopenharmony_ci		sd->gpio[0] &= ~mask;
59062306a36Sopenharmony_ci		sd->gpio[0] |= val;
59162306a36Sopenharmony_ci		reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,
59262306a36Sopenharmony_ci			~sd->gpio[0] << 8);
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	mask >>= 8;
59562306a36Sopenharmony_ci	val >>= 8;
59662306a36Sopenharmony_ci	if (mask) {
59762306a36Sopenharmony_ci		sd->gpio[1] &= ~mask;
59862306a36Sopenharmony_ci		sd->gpio[1] |= val;
59962306a36Sopenharmony_ci		reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,
60062306a36Sopenharmony_ci			~sd->gpio[1] << 8);
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void gpio_init(struct sd *sd,
60562306a36Sopenharmony_ci			const u8 *gpio)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	gpio_set(sd, *gpio++, 0x000f);
60862306a36Sopenharmony_ci	gpio_set(sd, *gpio++, 0x000f);
60962306a36Sopenharmony_ci	gpio_set(sd, *gpio++, 0x000f);
61062306a36Sopenharmony_ci	gpio_set(sd, *gpio++, 0x000f);
61162306a36Sopenharmony_ci	gpio_set(sd, *gpio, 0x000f);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic void bridge_init(struct sd *sd)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	static const struct ucbus_write_cmd clkfreq_cmd = {
61762306a36Sopenharmony_ci				0xf031, 0	/* SQ930_CLKFREQ_60MHZ */
61862306a36Sopenharmony_ci	};
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	gpio_set(sd, SQ930_GPIO_POWER, 0xff00);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void cmos_probe(struct gspca_dev *gspca_dev)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
62862306a36Sopenharmony_ci	int i;
62962306a36Sopenharmony_ci	const struct sensor_s *sensor;
63062306a36Sopenharmony_ci	static const u8 probe_order[] = {
63162306a36Sopenharmony_ci/*		SENSOR_LZ24BP,		(tested as ccd) */
63262306a36Sopenharmony_ci		SENSOR_OV9630,
63362306a36Sopenharmony_ci		SENSOR_MI0360,
63462306a36Sopenharmony_ci		SENSOR_OV7660,
63562306a36Sopenharmony_ci		SENSOR_MT9V111,
63662306a36Sopenharmony_ci	};
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
63962306a36Sopenharmony_ci		sensor = &sensor_tb[probe_order[i]];
64062306a36Sopenharmony_ci		ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);
64162306a36Sopenharmony_ci		gpio_init(sd, sensor->gpio);
64262306a36Sopenharmony_ci		msleep(100);
64362306a36Sopenharmony_ci		reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);
64462306a36Sopenharmony_ci		msleep(100);
64562306a36Sopenharmony_ci		if (gspca_dev->usb_buf[0] != 0)
64662306a36Sopenharmony_ci			break;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci	if (i >= ARRAY_SIZE(probe_order)) {
64962306a36Sopenharmony_ci		pr_err("Unknown sensor\n");
65062306a36Sopenharmony_ci		gspca_dev->usb_err = -EINVAL;
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci	sd->sensor = probe_order[i];
65462306a36Sopenharmony_ci	switch (sd->sensor) {
65562306a36Sopenharmony_ci	case SENSOR_OV7660:
65662306a36Sopenharmony_ci	case SENSOR_OV9630:
65762306a36Sopenharmony_ci		pr_err("Sensor %s not yet treated\n",
65862306a36Sopenharmony_ci		       sensor_tb[sd->sensor].name);
65962306a36Sopenharmony_ci		gspca_dev->usb_err = -EINVAL;
66062306a36Sopenharmony_ci		break;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic void mt9v111_init(struct gspca_dev *gspca_dev)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	int i, nwait;
66762306a36Sopenharmony_ci	static const u8 cmd_001b[] = {
66862306a36Sopenharmony_ci		0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,
66962306a36Sopenharmony_ci		0x00, 0x00, 0x00
67062306a36Sopenharmony_ci	};
67162306a36Sopenharmony_ci	static const u8 cmd_011b[][7] = {
67262306a36Sopenharmony_ci		{0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},
67362306a36Sopenharmony_ci		{0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},
67462306a36Sopenharmony_ci		{0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},
67562306a36Sopenharmony_ci		{0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},
67662306a36Sopenharmony_ci	};
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);
67962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {
68062306a36Sopenharmony_ci		reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],
68162306a36Sopenharmony_ci				ARRAY_SIZE(cmd_011b[0]));
68262306a36Sopenharmony_ci		msleep(400);
68362306a36Sopenharmony_ci		nwait = 20;
68462306a36Sopenharmony_ci		for (;;) {
68562306a36Sopenharmony_ci			reg_r(gspca_dev, 0x031b, 1);
68662306a36Sopenharmony_ci			if (gspca_dev->usb_buf[0] == 0
68762306a36Sopenharmony_ci			 || gspca_dev->usb_err != 0)
68862306a36Sopenharmony_ci				break;
68962306a36Sopenharmony_ci			if (--nwait < 0) {
69062306a36Sopenharmony_ci				gspca_dbg(gspca_dev, D_PROBE, "mt9v111_init timeout\n");
69162306a36Sopenharmony_ci				gspca_dev->usb_err = -ETIME;
69262306a36Sopenharmony_ci				return;
69362306a36Sopenharmony_ci			}
69462306a36Sopenharmony_ci			msleep(50);
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void global_init(struct sd *sd, int first_time)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	switch (sd->sensor) {
70262306a36Sopenharmony_ci	case SENSOR_ICX098BQ:
70362306a36Sopenharmony_ci		if (first_time)
70462306a36Sopenharmony_ci			ucbus_write(&sd->gspca_dev,
70562306a36Sopenharmony_ci					icx098bq_start_0,
70662306a36Sopenharmony_ci					8, 8);
70762306a36Sopenharmony_ci		gpio_init(sd, sensor_tb[sd->sensor].gpio);
70862306a36Sopenharmony_ci		break;
70962306a36Sopenharmony_ci	case SENSOR_LZ24BP:
71062306a36Sopenharmony_ci		if (sd->type != Creative_live_motion)
71162306a36Sopenharmony_ci			gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);
71262306a36Sopenharmony_ci		else
71362306a36Sopenharmony_ci			gpio_set(sd, 0, 0x00ff);
71462306a36Sopenharmony_ci		msleep(50);
71562306a36Sopenharmony_ci		if (first_time)
71662306a36Sopenharmony_ci			ucbus_write(&sd->gspca_dev,
71762306a36Sopenharmony_ci					lz24bp_start_0,
71862306a36Sopenharmony_ci					8, 8);
71962306a36Sopenharmony_ci		gpio_init(sd, sensor_tb[sd->sensor].gpio);
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	case SENSOR_MI0360:
72262306a36Sopenharmony_ci		if (first_time)
72362306a36Sopenharmony_ci			ucbus_write(&sd->gspca_dev,
72462306a36Sopenharmony_ci					mi0360_start_0,
72562306a36Sopenharmony_ci					ARRAY_SIZE(mi0360_start_0),
72662306a36Sopenharmony_ci					8);
72762306a36Sopenharmony_ci		gpio_init(sd, sensor_tb[sd->sensor].gpio);
72862306a36Sopenharmony_ci		gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	default:
73162306a36Sopenharmony_ci/*	case SENSOR_MT9V111: */
73262306a36Sopenharmony_ci		if (first_time)
73362306a36Sopenharmony_ci			mt9v111_init(&sd->gspca_dev);
73462306a36Sopenharmony_ci		else
73562306a36Sopenharmony_ci			gpio_init(sd, sensor_tb[sd->sensor].gpio);
73662306a36Sopenharmony_ci		break;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void lz24bp_ppl(struct sd *sd, u16 ppl)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct ucbus_write_cmd cmds[2] = {
74362306a36Sopenharmony_ci		{0xf810, ppl >> 8},
74462306a36Sopenharmony_ci		{0xf811, ppl}
74562306a36Sopenharmony_ci	};
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
75362306a36Sopenharmony_ci	int i, integclks, intstartclk, frameclks, min_frclk;
75462306a36Sopenharmony_ci	const struct sensor_s *sensor;
75562306a36Sopenharmony_ci	u16 cmd;
75662306a36Sopenharmony_ci	u8 buf[15];
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	integclks = expo;
75962306a36Sopenharmony_ci	i = 0;
76062306a36Sopenharmony_ci	cmd = SQ930_CTRL_SET_EXPOSURE;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	switch (sd->sensor) {
76362306a36Sopenharmony_ci	case SENSOR_ICX098BQ:			/* ccd */
76462306a36Sopenharmony_ci	case SENSOR_LZ24BP:
76562306a36Sopenharmony_ci		min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;
76662306a36Sopenharmony_ci		if (integclks >= min_frclk) {
76762306a36Sopenharmony_ci			intstartclk = 0;
76862306a36Sopenharmony_ci			frameclks = integclks;
76962306a36Sopenharmony_ci		} else {
77062306a36Sopenharmony_ci			intstartclk = min_frclk - integclks;
77162306a36Sopenharmony_ci			frameclks = min_frclk;
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci		buf[i++] = intstartclk >> 8;
77462306a36Sopenharmony_ci		buf[i++] = intstartclk;
77562306a36Sopenharmony_ci		buf[i++] = frameclks >> 8;
77662306a36Sopenharmony_ci		buf[i++] = frameclks;
77762306a36Sopenharmony_ci		buf[i++] = gain;
77862306a36Sopenharmony_ci		break;
77962306a36Sopenharmony_ci	default:				/* cmos */
78062306a36Sopenharmony_ci/*	case SENSOR_MI0360: */
78162306a36Sopenharmony_ci/*	case SENSOR_MT9V111: */
78262306a36Sopenharmony_ci		cmd |= 0x0100;
78362306a36Sopenharmony_ci		sensor = &sensor_tb[sd->sensor];
78462306a36Sopenharmony_ci		buf[i++] = sensor->i2c_addr;	/* i2c_slave_addr */
78562306a36Sopenharmony_ci		buf[i++] = 0x08;	/* 2 * ni2c */
78662306a36Sopenharmony_ci		buf[i++] = 0x09;	/* reg = shutter width */
78762306a36Sopenharmony_ci		buf[i++] = integclks >> 8; /* val H */
78862306a36Sopenharmony_ci		buf[i++] = sensor->i2c_dum;
78962306a36Sopenharmony_ci		buf[i++] = integclks;	/* val L */
79062306a36Sopenharmony_ci		buf[i++] = 0x35;	/* reg = global gain */
79162306a36Sopenharmony_ci		buf[i++] = 0x00;	/* val H */
79262306a36Sopenharmony_ci		buf[i++] = sensor->i2c_dum;
79362306a36Sopenharmony_ci		buf[i++] = 0x80 + gain / 2; /* val L */
79462306a36Sopenharmony_ci		buf[i++] = 0x00;
79562306a36Sopenharmony_ci		buf[i++] = 0x00;
79662306a36Sopenharmony_ci		buf[i++] = 0x00;
79762306a36Sopenharmony_ci		buf[i++] = 0x00;
79862306a36Sopenharmony_ci		buf[i++] = 0x83;
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci	reg_wb(gspca_dev, cmd, 0, buf, i);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci/* This function is called at probe time just before sd_init */
80562306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
80662306a36Sopenharmony_ci		const struct usb_device_id *id)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
80962306a36Sopenharmony_ci	struct cam *cam = &gspca_dev->cam;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	sd->sensor = id->driver_info >> 8;
81262306a36Sopenharmony_ci	sd->type = id->driver_info;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	cam->cam_mode = vga_mode;
81562306a36Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(vga_mode);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	cam->bulk = 1;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/* this function is called at probe and resume time */
82362306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	sd->gpio[0] = sd->gpio[1] = 0xff;	/* force gpio rewrite */
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/*fixme: is this needed for icx098bp and mi0360?
83062306a36Sopenharmony_ci	if (sd->sensor != SENSOR_LZ24BP)
83162306a36Sopenharmony_ci		reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);
83262306a36Sopenharmony_ci */
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);
83562306a36Sopenharmony_ci	if (gspca_dev->usb_err < 0)
83662306a36Sopenharmony_ci		return gspca_dev->usb_err;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci/* it returns:
83962306a36Sopenharmony_ci * 03 00 12 93 0b f6 c9 00	live! ultra
84062306a36Sopenharmony_ci * 03 00 07 93 0b f6 ca 00	live! ultra for notebook
84162306a36Sopenharmony_ci * 03 00 12 93 0b fe c8 00	Trust WB-3500T
84262306a36Sopenharmony_ci * 02 00 06 93 0b fe c8 00	Joy-IT 318S
84362306a36Sopenharmony_ci * 03 00 12 93 0b f6 cf 00	icam tracer - sensor icx098bq
84462306a36Sopenharmony_ci * 02 00 12 93 0b fe cf 00	ProQ Motion Webcam
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * byte
84762306a36Sopenharmony_ci * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)
84862306a36Sopenharmony_ci * 1: 00
84962306a36Sopenharmony_ci * 2: 06 / 07 / 12 = mode webcam? firmware??
85062306a36Sopenharmony_ci * 3: 93 chip = 930b (930b or 930c)
85162306a36Sopenharmony_ci * 4: 0b
85262306a36Sopenharmony_ci * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)
85362306a36Sopenharmony_ci * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?
85462306a36Sopenharmony_ci * 7: 00
85562306a36Sopenharmony_ci */
85662306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "info: %*ph\n", 8, gspca_dev->usb_buf);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	bridge_init(sd);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (sd->sensor == SENSOR_MI0360) {
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		/* no sensor probe for icam tracer */
86362306a36Sopenharmony_ci		if (gspca_dev->usb_buf[5] == 0xf6)	/* if ccd */
86462306a36Sopenharmony_ci			sd->sensor = SENSOR_ICX098BQ;
86562306a36Sopenharmony_ci		else
86662306a36Sopenharmony_ci			cmos_probe(gspca_dev);
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci	if (gspca_dev->usb_err >= 0) {
86962306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "Sensor %s\n",
87062306a36Sopenharmony_ci			  sensor_tb[sd->sensor].name);
87162306a36Sopenharmony_ci		global_init(sd, 1);
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	return gspca_dev->usb_err;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci/* send the start/stop commands to the webcam */
87762306a36Sopenharmony_cistatic void send_start(struct gspca_dev *gspca_dev)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
88062306a36Sopenharmony_ci	const struct cap_s *cap;
88162306a36Sopenharmony_ci	int mode;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
88462306a36Sopenharmony_ci	cap = &capconfig[sd->sensor][mode];
88562306a36Sopenharmony_ci	reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
88662306a36Sopenharmony_ci			0x0a00 | cap->cc_sizeid,
88762306a36Sopenharmony_ci			cap->cc_bytes, 32);
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic void send_stop(struct gspca_dev *gspca_dev)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/* function called at start time before URB creation */
89662306a36Sopenharmony_cistatic int sd_isoc_init(struct gspca_dev *gspca_dev)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	gspca_dev->cam.bulk_nurbs = 1;	/* there must be one URB only */
90162306a36Sopenharmony_ci	sd->do_ctrl = 0;
90262306a36Sopenharmony_ci	gspca_dev->cam.bulk_size = gspca_dev->pixfmt.width *
90362306a36Sopenharmony_ci			gspca_dev->pixfmt.height + 8;
90462306a36Sopenharmony_ci	return 0;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci/* start the capture */
90862306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
91162306a36Sopenharmony_ci	int mode;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	bridge_init(sd);
91462306a36Sopenharmony_ci	global_init(sd, 0);
91562306a36Sopenharmony_ci	msleep(100);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	switch (sd->sensor) {
91862306a36Sopenharmony_ci	case SENSOR_ICX098BQ:
91962306a36Sopenharmony_ci		ucbus_write(gspca_dev, icx098bq_start_0,
92062306a36Sopenharmony_ci				ARRAY_SIZE(icx098bq_start_0),
92162306a36Sopenharmony_ci				8);
92262306a36Sopenharmony_ci		ucbus_write(gspca_dev, icx098bq_start_1,
92362306a36Sopenharmony_ci				ARRAY_SIZE(icx098bq_start_1),
92462306a36Sopenharmony_ci				5);
92562306a36Sopenharmony_ci		ucbus_write(gspca_dev, icx098bq_start_2,
92662306a36Sopenharmony_ci				ARRAY_SIZE(icx098bq_start_2),
92762306a36Sopenharmony_ci				6);
92862306a36Sopenharmony_ci		msleep(50);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		/* 1st start */
93162306a36Sopenharmony_ci		send_start(gspca_dev);
93262306a36Sopenharmony_ci		gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
93362306a36Sopenharmony_ci		msleep(70);
93462306a36Sopenharmony_ci		reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
93562306a36Sopenharmony_ci		gpio_set(sd, 0x7f, 0x00ff);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		/* 2nd start */
93862306a36Sopenharmony_ci		send_start(gspca_dev);
93962306a36Sopenharmony_ci		gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
94062306a36Sopenharmony_ci		goto out;
94162306a36Sopenharmony_ci	case SENSOR_LZ24BP:
94262306a36Sopenharmony_ci		ucbus_write(gspca_dev, lz24bp_start_0,
94362306a36Sopenharmony_ci				ARRAY_SIZE(lz24bp_start_0),
94462306a36Sopenharmony_ci				8);
94562306a36Sopenharmony_ci		if (sd->type != Creative_live_motion)
94662306a36Sopenharmony_ci			ucbus_write(gspca_dev, lz24bp_start_1_gen,
94762306a36Sopenharmony_ci					ARRAY_SIZE(lz24bp_start_1_gen),
94862306a36Sopenharmony_ci					5);
94962306a36Sopenharmony_ci		else
95062306a36Sopenharmony_ci			ucbus_write(gspca_dev, lz24bp_start_1_clm,
95162306a36Sopenharmony_ci					ARRAY_SIZE(lz24bp_start_1_clm),
95262306a36Sopenharmony_ci					5);
95362306a36Sopenharmony_ci		ucbus_write(gspca_dev, lz24bp_start_2,
95462306a36Sopenharmony_ci				ARRAY_SIZE(lz24bp_start_2),
95562306a36Sopenharmony_ci				6);
95662306a36Sopenharmony_ci		mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
95762306a36Sopenharmony_ci		lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
95862306a36Sopenharmony_ci		msleep(10);
95962306a36Sopenharmony_ci		break;
96062306a36Sopenharmony_ci	case SENSOR_MI0360:
96162306a36Sopenharmony_ci		ucbus_write(gspca_dev, mi0360_start_0,
96262306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_start_0),
96362306a36Sopenharmony_ci				8);
96462306a36Sopenharmony_ci		i2c_write(sd, mi0360_init_23,
96562306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_init_23));
96662306a36Sopenharmony_ci		i2c_write(sd, mi0360_init_24,
96762306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_init_24));
96862306a36Sopenharmony_ci		i2c_write(sd, mi0360_init_25,
96962306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_init_25));
97062306a36Sopenharmony_ci		ucbus_write(gspca_dev, mi0360_start_1,
97162306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_start_1),
97262306a36Sopenharmony_ci				5);
97362306a36Sopenharmony_ci		i2c_write(sd, mi0360_start_2,
97462306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_start_2));
97562306a36Sopenharmony_ci		i2c_write(sd, mi0360_start_3,
97662306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_start_3));
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		/* 1st start */
97962306a36Sopenharmony_ci		send_start(gspca_dev);
98062306a36Sopenharmony_ci		msleep(60);
98162306a36Sopenharmony_ci		send_stop(gspca_dev);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci		i2c_write(sd,
98462306a36Sopenharmony_ci			mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
98562306a36Sopenharmony_ci		break;
98662306a36Sopenharmony_ci	default:
98762306a36Sopenharmony_ci/*	case SENSOR_MT9V111: */
98862306a36Sopenharmony_ci		ucbus_write(gspca_dev, mi0360_start_0,
98962306a36Sopenharmony_ci				ARRAY_SIZE(mi0360_start_0),
99062306a36Sopenharmony_ci				8);
99162306a36Sopenharmony_ci		i2c_write(sd, mt9v111_init_0,
99262306a36Sopenharmony_ci				ARRAY_SIZE(mt9v111_init_0));
99362306a36Sopenharmony_ci		i2c_write(sd, mt9v111_init_1,
99462306a36Sopenharmony_ci				ARRAY_SIZE(mt9v111_init_1));
99562306a36Sopenharmony_ci		i2c_write(sd, mt9v111_init_2,
99662306a36Sopenharmony_ci				ARRAY_SIZE(mt9v111_init_2));
99762306a36Sopenharmony_ci		ucbus_write(gspca_dev, mt9v111_start_1,
99862306a36Sopenharmony_ci				ARRAY_SIZE(mt9v111_start_1),
99962306a36Sopenharmony_ci				5);
100062306a36Sopenharmony_ci		i2c_write(sd, mt9v111_init_3,
100162306a36Sopenharmony_ci				ARRAY_SIZE(mt9v111_init_3));
100262306a36Sopenharmony_ci		i2c_write(sd, mt9v111_init_4,
100362306a36Sopenharmony_ci				ARRAY_SIZE(mt9v111_init_4));
100462306a36Sopenharmony_ci		break;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	send_start(gspca_dev);
100862306a36Sopenharmony_ciout:
100962306a36Sopenharmony_ci	msleep(1000);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (sd->sensor == SENSOR_MT9V111)
101262306a36Sopenharmony_ci		gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	sd->do_ctrl = 1;	/* set the exposure */
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	return gspca_dev->usb_err;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (sd->sensor == SENSOR_MT9V111)
102462306a36Sopenharmony_ci		gpio_set(sd, 0, SQ930_GPIO_DFL_LED);
102562306a36Sopenharmony_ci	send_stop(gspca_dev);
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci/* function called when the application gets a new frame */
102962306a36Sopenharmony_ci/* It sets the exposure if required and restart the bulk transfer. */
103062306a36Sopenharmony_cistatic void sd_dq_callback(struct gspca_dev *gspca_dev)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
103362306a36Sopenharmony_ci	int ret;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)
103662306a36Sopenharmony_ci		return;
103762306a36Sopenharmony_ci	sd->do_ctrl = 0;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
104062306a36Sopenharmony_ci			v4l2_ctrl_g_ctrl(sd->gain));
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	gspca_dev->cam.bulk_nurbs = 1;
104362306a36Sopenharmony_ci	ret = usb_submit_urb(gspca_dev->urb[0], GFP_KERNEL);
104462306a36Sopenharmony_ci	if (ret < 0)
104562306a36Sopenharmony_ci		pr_err("sd_dq_callback() err %d\n", ret);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* wait a little time, otherwise the webcam crashes */
104862306a36Sopenharmony_ci	msleep(100);
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
105262306a36Sopenharmony_ci			u8 *data,		/* isoc packet */
105362306a36Sopenharmony_ci			int len)		/* iso packet length */
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (sd->do_ctrl)
105862306a36Sopenharmony_ci		gspca_dev->cam.bulk_nurbs = 0;
105962306a36Sopenharmony_ci	gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
106062306a36Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
106162306a36Sopenharmony_ci	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct gspca_dev *gspca_dev =
106762306a36Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
106862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	gspca_dev->usb_err = 0;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (!gspca_dev->streaming)
107362306a36Sopenharmony_ci		return 0;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	switch (ctrl->id) {
107662306a36Sopenharmony_ci	case V4L2_CID_EXPOSURE:
107762306a36Sopenharmony_ci		setexposure(gspca_dev, ctrl->val, sd->gain->val);
107862306a36Sopenharmony_ci		break;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci	return gspca_dev->usb_err;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
108462306a36Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
108562306a36Sopenharmony_ci};
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
109062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
109362306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 2);
109462306a36Sopenharmony_ci	sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
109562306a36Sopenharmony_ci			V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
109662306a36Sopenharmony_ci	sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
109762306a36Sopenharmony_ci			V4L2_CID_GAIN, 1, 255, 1, 0x8d);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	if (hdl->error) {
110062306a36Sopenharmony_ci		pr_err("Could not initialize controls\n");
110162306a36Sopenharmony_ci		return hdl->error;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci	v4l2_ctrl_cluster(2, &sd->exposure);
110462306a36Sopenharmony_ci	return 0;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci/* sub-driver description */
110862306a36Sopenharmony_cistatic const struct sd_desc sd_desc = {
110962306a36Sopenharmony_ci	.name   = MODULE_NAME,
111062306a36Sopenharmony_ci	.config = sd_config,
111162306a36Sopenharmony_ci	.init   = sd_init,
111262306a36Sopenharmony_ci	.init_controls = sd_init_controls,
111362306a36Sopenharmony_ci	.isoc_init = sd_isoc_init,
111462306a36Sopenharmony_ci	.start  = sd_start,
111562306a36Sopenharmony_ci	.stopN  = sd_stopN,
111662306a36Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
111762306a36Sopenharmony_ci	.dq_callback = sd_dq_callback,
111862306a36Sopenharmony_ci};
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci/* Table of supported USB devices */
112162306a36Sopenharmony_ci#define ST(sensor, type) \
112262306a36Sopenharmony_ci	.driver_info = (SENSOR_ ## sensor << 8) \
112362306a36Sopenharmony_ci			| (type)
112462306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = {
112562306a36Sopenharmony_ci	{USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},
112662306a36Sopenharmony_ci	{USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},
112762306a36Sopenharmony_ci	{USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},
112862306a36Sopenharmony_ci	{USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},
112962306a36Sopenharmony_ci	{USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},
113062306a36Sopenharmony_ci	{USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},
113162306a36Sopenharmony_ci	{}
113262306a36Sopenharmony_ci};
113362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci/* -- device connect -- */
113762306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
113862306a36Sopenharmony_ci		const struct usb_device_id *id)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
114162306a36Sopenharmony_ci			THIS_MODULE);
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic struct usb_driver sd_driver = {
114562306a36Sopenharmony_ci	.name	    = MODULE_NAME,
114662306a36Sopenharmony_ci	.id_table   = device_table,
114762306a36Sopenharmony_ci	.probe	    = sd_probe,
114862306a36Sopenharmony_ci	.disconnect = gspca_disconnect,
114962306a36Sopenharmony_ci#ifdef CONFIG_PM
115062306a36Sopenharmony_ci	.suspend    = gspca_suspend,
115162306a36Sopenharmony_ci	.resume     = gspca_resume,
115262306a36Sopenharmony_ci	.reset_resume = gspca_resume,
115362306a36Sopenharmony_ci#endif
115462306a36Sopenharmony_ci};
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_cimodule_usb_driver(sd_driver);
1157