xref: /linux/drivers/media/usb/gspca/spca561.c (revision 24e8a2ca1f74574ad2ed1ac7af0260dd90fd911e)
1*fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26a7eba24SJean-Francois Moine /*
36a7eba24SJean-Francois Moine  * Sunplus spca561 subdriver
46a7eba24SJean-Francois Moine  *
56a7eba24SJean-Francois Moine  * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
66a7eba24SJean-Francois Moine  *
76a7eba24SJean-Francois Moine  * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
86a7eba24SJean-Francois Moine  */
96a7eba24SJean-Francois Moine 
10133a9fe9SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11133a9fe9SJoe Perches 
126a7eba24SJean-Francois Moine #define MODULE_NAME "spca561"
136a7eba24SJean-Francois Moine 
14436c2c53SHans de Goede #include <linux/input.h>
156a7eba24SJean-Francois Moine #include "gspca.h"
166a7eba24SJean-Francois Moine 
176a7eba24SJean-Francois Moine MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
186a7eba24SJean-Francois Moine MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
196a7eba24SJean-Francois Moine MODULE_LICENSE("GPL");
206a7eba24SJean-Francois Moine 
213fa24bf5SHans Verkuil #define EXPOSURE_MAX (2047 + 325)
223fa24bf5SHans Verkuil 
236a7eba24SJean-Francois Moine /* specific webcam descriptor */
246a7eba24SJean-Francois Moine struct sd {
256a7eba24SJean-Francois Moine 	struct gspca_dev gspca_dev;	/* !! must be the first item */
266a7eba24SJean-Francois Moine 
273fa24bf5SHans Verkuil 	struct { /* hue/contrast control cluster */
283fa24bf5SHans Verkuil 		struct v4l2_ctrl *contrast;
293fa24bf5SHans Verkuil 		struct v4l2_ctrl *hue;
303fa24bf5SHans Verkuil 	};
313fa24bf5SHans Verkuil 	struct v4l2_ctrl *autogain;
326a7eba24SJean-Francois Moine 
33d698dc6bSJean-Francois Moine #define EXPO12A_DEF 3
34d698dc6bSJean-Francois Moine 	__u8 expo12a;		/* expo/gain? for rev 12a */
35d698dc6bSJean-Francois Moine 
366a7eba24SJean-Francois Moine 	__u8 chip_revision;
377879d459SJean-Francois Moine #define Rev012A 0
387879d459SJean-Francois Moine #define Rev072A 1
397879d459SJean-Francois Moine 
406a7eba24SJean-Francois Moine 	signed char ag_cnt;
416a7eba24SJean-Francois Moine #define AG_CNT_START 13
426a7eba24SJean-Francois Moine };
436a7eba24SJean-Francois Moine 
44cc611b8aSJean-Francois Moine static const struct v4l2_pix_format sif_012a_mode[] = {
45c2446b3eSJean-Francois Moine 	{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
46c2446b3eSJean-Francois Moine 		.bytesperline = 160,
47c2446b3eSJean-Francois Moine 		.sizeimage = 160 * 120,
48c2446b3eSJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
49c2446b3eSJean-Francois Moine 		.priv = 3},
50c2446b3eSJean-Francois Moine 	{176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
51c2446b3eSJean-Francois Moine 		.bytesperline = 176,
52c2446b3eSJean-Francois Moine 		.sizeimage = 176 * 144,
53c2446b3eSJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
54c2446b3eSJean-Francois Moine 		.priv = 2},
55c2446b3eSJean-Francois Moine 	{320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
56c2446b3eSJean-Francois Moine 		.bytesperline = 320,
57c2446b3eSJean-Francois Moine 		.sizeimage = 320 * 240 * 4 / 8,
58c2446b3eSJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
59c2446b3eSJean-Francois Moine 		.priv = 1},
60c2446b3eSJean-Francois Moine 	{352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
61c2446b3eSJean-Francois Moine 		.bytesperline = 352,
62c2446b3eSJean-Francois Moine 		.sizeimage = 352 * 288 * 4 / 8,
63c2446b3eSJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
64c2446b3eSJean-Francois Moine 		.priv = 0},
656a7eba24SJean-Francois Moine };
666a7eba24SJean-Francois Moine 
67cc611b8aSJean-Francois Moine static const struct v4l2_pix_format sif_072a_mode[] = {
68b77c0046SJean-Francois Moine 	{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
69b77c0046SJean-Francois Moine 		.bytesperline = 160,
70b77c0046SJean-Francois Moine 		.sizeimage = 160 * 120,
71b77c0046SJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
72b77c0046SJean-Francois Moine 		.priv = 3},
73b77c0046SJean-Francois Moine 	{176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
74b77c0046SJean-Francois Moine 		.bytesperline = 176,
75b77c0046SJean-Francois Moine 		.sizeimage = 176 * 144,
76b77c0046SJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
77b77c0046SJean-Francois Moine 		.priv = 2},
78b77c0046SJean-Francois Moine 	{320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
79b77c0046SJean-Francois Moine 		.bytesperline = 320,
80b77c0046SJean-Francois Moine 		.sizeimage = 320 * 240,
81b77c0046SJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
82b77c0046SJean-Francois Moine 		.priv = 1},
83b77c0046SJean-Francois Moine 	{352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
84b77c0046SJean-Francois Moine 		.bytesperline = 352,
85b77c0046SJean-Francois Moine 		.sizeimage = 352 * 288,
86b77c0046SJean-Francois Moine 		.colorspace = V4L2_COLORSPACE_SRGB,
87b77c0046SJean-Francois Moine 		.priv = 0},
88b77c0046SJean-Francois Moine };
89b77c0046SJean-Francois Moine 
906a7eba24SJean-Francois Moine /*
916a7eba24SJean-Francois Moine  * Initialization data
926a7eba24SJean-Francois Moine  * I'm not very sure how to split initialization from open data
936a7eba24SJean-Francois Moine  * chunks. For now, we'll consider everything as initialization
946a7eba24SJean-Francois Moine  */
956a7eba24SJean-Francois Moine /* Frame packet header offsets for the spca561 */
966a7eba24SJean-Francois Moine #define SPCA561_OFFSET_SNAP 1
976a7eba24SJean-Francois Moine #define SPCA561_OFFSET_TYPE 2
986a7eba24SJean-Francois Moine #define SPCA561_OFFSET_COMPRESS 3
996a7eba24SJean-Francois Moine #define SPCA561_OFFSET_FRAMSEQ   4
1006a7eba24SJean-Francois Moine #define SPCA561_OFFSET_GPIO 5
1016a7eba24SJean-Francois Moine #define SPCA561_OFFSET_USBBUFF 6
1026a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2GRAVE 7
1036a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2RAVE 8
1046a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2BAVE 9
1056a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN2GBAVE 10
1066a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1GRAVE 11
1076a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1RAVE 12
1086a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1BAVE 13
1096a7eba24SJean-Francois Moine #define SPCA561_OFFSET_WIN1GBAVE 14
1106a7eba24SJean-Francois Moine #define SPCA561_OFFSET_FREQ 15
1116a7eba24SJean-Francois Moine #define SPCA561_OFFSET_VSYNC 16
1126a7eba24SJean-Francois Moine #define SPCA561_INDEX_I2C_BASE 0x8800
1136a7eba24SJean-Francois Moine #define SPCA561_SNAPBIT 0x20
1146a7eba24SJean-Francois Moine #define SPCA561_SNAPCTRL 0x40
1156a7eba24SJean-Francois Moine 
1160dbc2c16SJean-Francois Moine static const u16 rev72a_reset[][2] = {
1176a7eba24SJean-Francois Moine 	{0x0000, 0x8114},	/* Software GPIO output data */
1186a7eba24SJean-Francois Moine 	{0x0001, 0x8114},	/* Software GPIO output data */
1196a7eba24SJean-Francois Moine 	{0x0000, 0x8112},	/* Some kind of reset */
1200dbc2c16SJean-Francois Moine 	{}
1210dbc2c16SJean-Francois Moine };
1220dbc2c16SJean-Francois Moine static const __u16 rev72a_init_data1[][2] = {
1236a7eba24SJean-Francois Moine 	{0x0003, 0x8701},	/* PCLK clock delay adjustment */
1246a7eba24SJean-Francois Moine 	{0x0001, 0x8703},	/* HSYNC from cmos inverted */
1256a7eba24SJean-Francois Moine 	{0x0011, 0x8118},	/* Enable and conf sensor */
1266a7eba24SJean-Francois Moine 	{0x0001, 0x8118},	/* Conf sensor */
1276a7eba24SJean-Francois Moine 	{0x0092, 0x8804},	/* I know nothing about these */
1286a7eba24SJean-Francois Moine 	{0x0010, 0x8802},	/* 0x88xx registers, so I won't */
129f8a04a6fSJean-Francois Moine 	{}
130f8a04a6fSJean-Francois Moine };
1310dbc2c16SJean-Francois Moine static const u16 rev72a_init_sensor1[][2] = {
1320dbc2c16SJean-Francois Moine 	{0x0001, 0x000d},
1330dbc2c16SJean-Francois Moine 	{0x0002, 0x0018},
1340dbc2c16SJean-Francois Moine 	{0x0004, 0x0165},
1350dbc2c16SJean-Francois Moine 	{0x0005, 0x0021},
1360dbc2c16SJean-Francois Moine 	{0x0007, 0x00aa},
1370dbc2c16SJean-Francois Moine 	{0x0020, 0x1504},
1380dbc2c16SJean-Francois Moine 	{0x0039, 0x0002},
1390dbc2c16SJean-Francois Moine 	{0x0035, 0x0010},
1400dbc2c16SJean-Francois Moine 	{0x0009, 0x1049},
1410dbc2c16SJean-Francois Moine 	{0x0028, 0x000b},
1420dbc2c16SJean-Francois Moine 	{0x003b, 0x000f},
1430dbc2c16SJean-Francois Moine 	{0x003c, 0x0000},
144f8a04a6fSJean-Francois Moine 	{}
145f8a04a6fSJean-Francois Moine };
146f8a04a6fSJean-Francois Moine static const __u16 rev72a_init_data2[][2] = {
1476a7eba24SJean-Francois Moine 	{0x0018, 0x8601},	/* Pixel/line selection for color separation */
1486a7eba24SJean-Francois Moine 	{0x0000, 0x8602},	/* Optical black level for user setting */
1496a7eba24SJean-Francois Moine 	{0x0060, 0x8604},	/* Optical black horizontal offset */
1506a7eba24SJean-Francois Moine 	{0x0002, 0x8605},	/* Optical black vertical offset */
1516a7eba24SJean-Francois Moine 	{0x0000, 0x8603},	/* Non-automatic optical black level */
1526a7eba24SJean-Francois Moine 	{0x0002, 0x865b},	/* Horizontal offset for valid pixels */
1536a7eba24SJean-Francois Moine 	{0x0000, 0x865f},	/* Vertical valid pixels window (x2) */
1546a7eba24SJean-Francois Moine 	{0x00b0, 0x865d},	/* Horizontal valid pixels window (x2) */
1556a7eba24SJean-Francois Moine 	{0x0090, 0x865e},	/* Vertical valid lines window (x2) */
1566a7eba24SJean-Francois Moine 	{0x00e0, 0x8406},	/* Memory buffer threshold */
1576a7eba24SJean-Francois Moine 	{0x0000, 0x8660},	/* Compensation memory stuff */
1586a7eba24SJean-Francois Moine 	{0x0002, 0x8201},	/* Output address for r/w serial EEPROM */
1596a7eba24SJean-Francois Moine 	{0x0008, 0x8200},	/* Clear valid bit for serial EEPROM */
1606a7eba24SJean-Francois Moine 	{0x0001, 0x8200},	/* OprMode to be executed by hardware */
1610dbc2c16SJean-Francois Moine /* from ms-win */
1620dbc2c16SJean-Francois Moine 	{0x0000, 0x8611},	/* R offset for white balance */
1630dbc2c16SJean-Francois Moine 	{0x00fd, 0x8612},	/* Gr offset for white balance */
1640dbc2c16SJean-Francois Moine 	{0x0003, 0x8613},	/* B offset for white balance */
1656a7eba24SJean-Francois Moine 	{0x0000, 0x8614},	/* Gb offset for white balance */
1665b7ed28eSJean-Francois Moine /* from ms-win */
1675b7ed28eSJean-Francois Moine 	{0x0035, 0x8651},	/* R gain for white balance */
1685b7ed28eSJean-Francois Moine 	{0x0040, 0x8652},	/* Gr gain for white balance */
1695b7ed28eSJean-Francois Moine 	{0x005f, 0x8653},	/* B gain for white balance */
1705b7ed28eSJean-Francois Moine 	{0x0040, 0x8654},	/* Gb gain for white balance */
1716a7eba24SJean-Francois Moine 	{0x0002, 0x8502},	/* Maximum average bit rate stuff */
1726a7eba24SJean-Francois Moine 	{0x0011, 0x8802},
1730dbc2c16SJean-Francois Moine 
1746a7eba24SJean-Francois Moine 	{0x0087, 0x8700},	/* Set master clock (96Mhz????) */
1756a7eba24SJean-Francois Moine 	{0x0081, 0x8702},	/* Master clock output enable */
1766a7eba24SJean-Francois Moine 
1776a7eba24SJean-Francois Moine 	{0x0000, 0x8500},	/* Set image type (352x288 no compression) */
1786a7eba24SJean-Francois Moine 	/* Originally was 0x0010 (352x288 compression) */
1796a7eba24SJean-Francois Moine 
1806a7eba24SJean-Francois Moine 	{0x0002, 0x865b},	/* Horizontal offset for valid pixels */
1816a7eba24SJean-Francois Moine 	{0x0003, 0x865c},	/* Vertical offset for valid lines */
182f8a04a6fSJean-Francois Moine 	{}
183f8a04a6fSJean-Francois Moine };
1840dbc2c16SJean-Francois Moine static const u16 rev72a_init_sensor2[][2] = {
1850dbc2c16SJean-Francois Moine 	{0x0003, 0x0121},
1860dbc2c16SJean-Francois Moine 	{0x0004, 0x0165},
1870dbc2c16SJean-Francois Moine 	{0x0005, 0x002f},	/* blanking control column */
1880dbc2c16SJean-Francois Moine 	{0x0006, 0x0000},	/* blanking mode row*/
1890dbc2c16SJean-Francois Moine 	{0x000a, 0x0002},
1900dbc2c16SJean-Francois Moine 	{0x0009, 0x1061},	/* setexposure times && pixel clock
1916a7eba24SJean-Francois Moine 				 * 0001 0 | 000 0110 0001 */
1920dbc2c16SJean-Francois Moine 	{0x0035, 0x0014},
1936a7eba24SJean-Francois Moine 	{}
1946a7eba24SJean-Francois Moine };
1956a7eba24SJean-Francois Moine 
1966a7eba24SJean-Francois Moine /******************** QC Express etch2 stuff ********************/
197a5ae2062SJean-Francois Moine static const __u16 Pb100_1map8300[][2] = {
1986a7eba24SJean-Francois Moine 	/* reg, value */
1996a7eba24SJean-Francois Moine 	{0x8320, 0x3304},
2006a7eba24SJean-Francois Moine 
2016a7eba24SJean-Francois Moine 	{0x8303, 0x0125},	/* image area */
2026a7eba24SJean-Francois Moine 	{0x8304, 0x0169},
2036a7eba24SJean-Francois Moine 	{0x8328, 0x000b},
2047879d459SJean-Francois Moine 	{0x833c, 0x0001},		/*fixme: win:07*/
2056a7eba24SJean-Francois Moine 
2067879d459SJean-Francois Moine 	{0x832f, 0x1904},		/*fixme: was 0419*/
2076a7eba24SJean-Francois Moine 	{0x8307, 0x00aa},
2086a7eba24SJean-Francois Moine 	{0x8301, 0x0003},
2096a7eba24SJean-Francois Moine 	{0x8302, 0x000e},
2106a7eba24SJean-Francois Moine 	{}
2116a7eba24SJean-Francois Moine };
212a5ae2062SJean-Francois Moine static const __u16 Pb100_2map8300[][2] = {
2136a7eba24SJean-Francois Moine 	/* reg, value */
2146a7eba24SJean-Francois Moine 	{0x8339, 0x0000},
2156a7eba24SJean-Francois Moine 	{0x8307, 0x00aa},
2166a7eba24SJean-Francois Moine 	{}
2176a7eba24SJean-Francois Moine };
2186a7eba24SJean-Francois Moine 
219a5ae2062SJean-Francois Moine static const __u16 spca561_161rev12A_data1[][2] = {
2206b33e5e7SHans de Goede 	{0x29, 0x8118},		/* Control register (various enable bits) */
2216b33e5e7SHans de Goede 	{0x08, 0x8114},		/* GPIO: Led off */
2226b33e5e7SHans de Goede 	{0x0e, 0x8112},		/* 0x0e stream off 0x3e stream on */
2236c9d3c59SJean-Francois Moine 	{0x00, 0x8102},		/* white balance - new */
2246a7eba24SJean-Francois Moine 	{0x92, 0x8804},
2256a7eba24SJean-Francois Moine 	{0x04, 0x8802},		/* windows uses 08 */
2266a7eba24SJean-Francois Moine 	{}
2276a7eba24SJean-Francois Moine };
228a5ae2062SJean-Francois Moine static const __u16 spca561_161rev12A_data2[][2] = {
2296a7eba24SJean-Francois Moine 	{0x21, 0x8118},
2306a7eba24SJean-Francois Moine 	{0x10, 0x8500},
2316a7eba24SJean-Francois Moine 	{0x07, 0x8601},
2326a7eba24SJean-Francois Moine 	{0x07, 0x8602},
2336a7eba24SJean-Francois Moine 	{0x04, 0x8501},
2346a7eba24SJean-Francois Moine 
2356a7eba24SJean-Francois Moine 	{0x07, 0x8201},		/* windows uses 02 */
2366a7eba24SJean-Francois Moine 	{0x08, 0x8200},
2376a7eba24SJean-Francois Moine 	{0x01, 0x8200},
2386a7eba24SJean-Francois Moine 
2396a7eba24SJean-Francois Moine 	{0x90, 0x8604},
2406a7eba24SJean-Francois Moine 	{0x00, 0x8605},
2416a7eba24SJean-Francois Moine 	{0xb0, 0x8603},
2426a7eba24SJean-Francois Moine 
2436a7eba24SJean-Francois Moine 	/* sensor gains */
2446c9d3c59SJean-Francois Moine 	{0x07, 0x8601},		/* white balance - new */
2456c9d3c59SJean-Francois Moine 	{0x07, 0x8602},		/* white balance - new */
2466a7eba24SJean-Francois Moine 	{0x00, 0x8610},		/* *red */
2476a7eba24SJean-Francois Moine 	{0x00, 0x8611},		/* 3f   *green */
2486a7eba24SJean-Francois Moine 	{0x00, 0x8612},		/* green *blue */
2496a7eba24SJean-Francois Moine 	{0x00, 0x8613},		/* blue *green */
2506c9d3c59SJean-Francois Moine 	{0x43, 0x8614},		/* green *red - white balance - was 0x35 */
2516c9d3c59SJean-Francois Moine 	{0x40, 0x8615},		/* 40   *green - white balance - was 0x35 */
2526c9d3c59SJean-Francois Moine 	{0x71, 0x8616},		/* 7a   *blue - white balance - was 0x35 */
2536c9d3c59SJean-Francois Moine 	{0x40, 0x8617},		/* 40   *green - white balance - was 0x35 */
2546a7eba24SJean-Francois Moine 
2556a7eba24SJean-Francois Moine 	{0x0c, 0x8620},		/* 0c */
2566a7eba24SJean-Francois Moine 	{0xc8, 0x8631},		/* c8 */
2576a7eba24SJean-Francois Moine 	{0xc8, 0x8634},		/* c8 */
2586a7eba24SJean-Francois Moine 	{0x23, 0x8635},		/* 23 */
2596a7eba24SJean-Francois Moine 	{0x1f, 0x8636},		/* 1f */
2606a7eba24SJean-Francois Moine 	{0xdd, 0x8637},		/* dd */
2616a7eba24SJean-Francois Moine 	{0xe1, 0x8638},		/* e1 */
2626a7eba24SJean-Francois Moine 	{0x1d, 0x8639},		/* 1d */
2636a7eba24SJean-Francois Moine 	{0x21, 0x863a},		/* 21 */
2646a7eba24SJean-Francois Moine 	{0xe3, 0x863b},		/* e3 */
2656a7eba24SJean-Francois Moine 	{0xdf, 0x863c},		/* df */
2666a7eba24SJean-Francois Moine 	{0xf0, 0x8505},
2676a7eba24SJean-Francois Moine 	{0x32, 0x850a},
2687879d459SJean-Francois Moine /*	{0x99, 0x8700},		 * - white balance - new (removed) */
2696b33e5e7SHans de Goede 	/* HDG we used to do this in stop0, making the init state and the state
2706b33e5e7SHans de Goede 	   after a start / stop different, so do this here instead. */
2716b33e5e7SHans de Goede 	{0x29, 0x8118},
2726a7eba24SJean-Francois Moine 	{}
2736a7eba24SJean-Francois Moine };
2746a7eba24SJean-Francois Moine 
275c93396e1STheodore Kilgore static void reg_w_val(struct gspca_dev *gspca_dev, __u16 index, __u8 value)
2766a7eba24SJean-Francois Moine {
27735dc1b4cSJean-Francois Moine 	int ret;
278c93396e1STheodore Kilgore 	struct usb_device *dev = gspca_dev->dev;
2796a7eba24SJean-Francois Moine 
28035dc1b4cSJean-Francois Moine 	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
28135dc1b4cSJean-Francois Moine 			      0,		/* request */
28235dc1b4cSJean-Francois Moine 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
28335dc1b4cSJean-Francois Moine 			      value, index, NULL, 0, 500);
28437d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_USBO, "reg write: 0x%02x:0x%02x\n",
28537d5efb0SJoe Perches 		  index, value);
28635dc1b4cSJean-Francois Moine 	if (ret < 0)
287133a9fe9SJoe Perches 		pr_err("reg write: error %d\n", ret);
28835dc1b4cSJean-Francois Moine }
28935dc1b4cSJean-Francois Moine 
29035dc1b4cSJean-Francois Moine static void write_vector(struct gspca_dev *gspca_dev,
29135dc1b4cSJean-Francois Moine 			const __u16 data[][2])
29235dc1b4cSJean-Francois Moine {
29335dc1b4cSJean-Francois Moine 	int i;
29435dc1b4cSJean-Francois Moine 
29535dc1b4cSJean-Francois Moine 	i = 0;
29635dc1b4cSJean-Francois Moine 	while (data[i][1] != 0) {
297c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, data[i][1], data[i][0]);
2986a7eba24SJean-Francois Moine 		i++;
2996a7eba24SJean-Francois Moine 	}
3006a7eba24SJean-Francois Moine }
30135dc1b4cSJean-Francois Moine 
30235dc1b4cSJean-Francois Moine /* read 'len' bytes to gspca_dev->usb_buf */
30335dc1b4cSJean-Francois Moine static void reg_r(struct gspca_dev *gspca_dev,
30435dc1b4cSJean-Francois Moine 		  __u16 index, __u16 length)
30535dc1b4cSJean-Francois Moine {
30635dc1b4cSJean-Francois Moine 	usb_control_msg(gspca_dev->dev,
30735dc1b4cSJean-Francois Moine 			usb_rcvctrlpipe(gspca_dev->dev, 0),
30835dc1b4cSJean-Francois Moine 			0,			/* request */
30935dc1b4cSJean-Francois Moine 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
31035dc1b4cSJean-Francois Moine 			0,			/* value */
31135dc1b4cSJean-Francois Moine 			index, gspca_dev->usb_buf, length, 500);
31235dc1b4cSJean-Francois Moine }
31335dc1b4cSJean-Francois Moine 
31435dc1b4cSJean-Francois Moine /* write 'len' bytes from gspca_dev->usb_buf */
31535dc1b4cSJean-Francois Moine static void reg_w_buf(struct gspca_dev *gspca_dev,
31635dc1b4cSJean-Francois Moine 		      __u16 index, __u16 len)
31735dc1b4cSJean-Francois Moine {
31835dc1b4cSJean-Francois Moine 	usb_control_msg(gspca_dev->dev,
31935dc1b4cSJean-Francois Moine 			usb_sndctrlpipe(gspca_dev->dev, 0),
32035dc1b4cSJean-Francois Moine 			0,			/* request */
32135dc1b4cSJean-Francois Moine 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
32235dc1b4cSJean-Francois Moine 			0,			/* value */
32335dc1b4cSJean-Francois Moine 			index, gspca_dev->usb_buf, len, 500);
32435dc1b4cSJean-Francois Moine }
32535dc1b4cSJean-Francois Moine 
32635dc1b4cSJean-Francois Moine static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
32735dc1b4cSJean-Francois Moine {
32835dc1b4cSJean-Francois Moine 	int retry = 60;
32935dc1b4cSJean-Francois Moine 
330c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8801, reg);
331c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8805, value);
332c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8800, value >> 8);
33335dc1b4cSJean-Francois Moine 	do {
33435dc1b4cSJean-Francois Moine 		reg_r(gspca_dev, 0x8803, 1);
33535dc1b4cSJean-Francois Moine 		if (!gspca_dev->usb_buf[0])
33635dc1b4cSJean-Francois Moine 			return;
3370dbc2c16SJean-Francois Moine 		msleep(10);
33835dc1b4cSJean-Francois Moine 	} while (--retry);
33935dc1b4cSJean-Francois Moine }
34035dc1b4cSJean-Francois Moine 
34135dc1b4cSJean-Francois Moine static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
34235dc1b4cSJean-Francois Moine {
34335dc1b4cSJean-Francois Moine 	int retry = 60;
34435dc1b4cSJean-Francois Moine 	__u8 value;
34535dc1b4cSJean-Francois Moine 
346c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8804, 0x92);
347c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8801, reg);
348c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8802, mode | 0x01);
34935dc1b4cSJean-Francois Moine 	do {
35035dc1b4cSJean-Francois Moine 		reg_r(gspca_dev, 0x8803, 1);
35135dc1b4cSJean-Francois Moine 		if (!gspca_dev->usb_buf[0]) {
35235dc1b4cSJean-Francois Moine 			reg_r(gspca_dev, 0x8800, 1);
35335dc1b4cSJean-Francois Moine 			value = gspca_dev->usb_buf[0];
35435dc1b4cSJean-Francois Moine 			reg_r(gspca_dev, 0x8805, 1);
35535dc1b4cSJean-Francois Moine 			return ((int) value << 8) | gspca_dev->usb_buf[0];
35635dc1b4cSJean-Francois Moine 		}
3570dbc2c16SJean-Francois Moine 		msleep(10);
35835dc1b4cSJean-Francois Moine 	} while (--retry);
35935dc1b4cSJean-Francois Moine 	return -1;
36035dc1b4cSJean-Francois Moine }
36135dc1b4cSJean-Francois Moine 
36235dc1b4cSJean-Francois Moine static void sensor_mapwrite(struct gspca_dev *gspca_dev,
36335dc1b4cSJean-Francois Moine 			    const __u16 (*sensormap)[2])
36435dc1b4cSJean-Francois Moine {
36535dc1b4cSJean-Francois Moine 	while ((*sensormap)[0]) {
36635dc1b4cSJean-Francois Moine 		gspca_dev->usb_buf[0] = (*sensormap)[1];
36735dc1b4cSJean-Francois Moine 		gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
36835dc1b4cSJean-Francois Moine 		reg_w_buf(gspca_dev, (*sensormap)[0], 2);
36935dc1b4cSJean-Francois Moine 		sensormap++;
37035dc1b4cSJean-Francois Moine 	}
37135dc1b4cSJean-Francois Moine }
37235dc1b4cSJean-Francois Moine 
373f8a04a6fSJean-Francois Moine static void write_sensor_72a(struct gspca_dev *gspca_dev,
374f8a04a6fSJean-Francois Moine 			    const __u16 (*sensor)[2])
375f8a04a6fSJean-Francois Moine {
376f8a04a6fSJean-Francois Moine 	while ((*sensor)[0]) {
377f8a04a6fSJean-Francois Moine 		i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
378f8a04a6fSJean-Francois Moine 		sensor++;
379f8a04a6fSJean-Francois Moine 	}
380f8a04a6fSJean-Francois Moine }
381f8a04a6fSJean-Francois Moine 
3826a7eba24SJean-Francois Moine static void init_161rev12A(struct gspca_dev *gspca_dev)
3836a7eba24SJean-Francois Moine {
3846a7eba24SJean-Francois Moine 	write_vector(gspca_dev, spca561_161rev12A_data1);
3856a7eba24SJean-Francois Moine 	sensor_mapwrite(gspca_dev, Pb100_1map8300);
3867879d459SJean-Francois Moine /*fixme: should be in sd_start*/
3876a7eba24SJean-Francois Moine 	write_vector(gspca_dev, spca561_161rev12A_data2);
3886a7eba24SJean-Francois Moine 	sensor_mapwrite(gspca_dev, Pb100_2map8300);
3896a7eba24SJean-Francois Moine }
3906a7eba24SJean-Francois Moine 
3916a7eba24SJean-Francois Moine /* this function is called at probe time */
3926a7eba24SJean-Francois Moine static int sd_config(struct gspca_dev *gspca_dev,
3936a7eba24SJean-Francois Moine 		     const struct usb_device_id *id)
3946a7eba24SJean-Francois Moine {
3956a7eba24SJean-Francois Moine 	struct sd *sd = (struct sd *) gspca_dev;
3966a7eba24SJean-Francois Moine 	struct cam *cam;
3976a7eba24SJean-Francois Moine 	__u16 vendor, product;
3986a7eba24SJean-Francois Moine 	__u8 data1, data2;
3996a7eba24SJean-Francois Moine 
4006a7eba24SJean-Francois Moine 	/* Read frm global register the USB product and vendor IDs, just to
4016a7eba24SJean-Francois Moine 	 * prove that we can communicate with the device.  This works, which
4026a7eba24SJean-Francois Moine 	 * confirms at we are communicating properly and that the device
4036a7eba24SJean-Francois Moine 	 * is a 561. */
404739570bbSJean-Francois Moine 	reg_r(gspca_dev, 0x8104, 1);
405739570bbSJean-Francois Moine 	data1 = gspca_dev->usb_buf[0];
406739570bbSJean-Francois Moine 	reg_r(gspca_dev, 0x8105, 1);
407739570bbSJean-Francois Moine 	data2 = gspca_dev->usb_buf[0];
4086a7eba24SJean-Francois Moine 	vendor = (data2 << 8) | data1;
409739570bbSJean-Francois Moine 	reg_r(gspca_dev, 0x8106, 1);
410739570bbSJean-Francois Moine 	data1 = gspca_dev->usb_buf[0];
411739570bbSJean-Francois Moine 	reg_r(gspca_dev, 0x8107, 1);
412739570bbSJean-Francois Moine 	data2 = gspca_dev->usb_buf[0];
4136a7eba24SJean-Francois Moine 	product = (data2 << 8) | data1;
4146a7eba24SJean-Francois Moine 	if (vendor != id->idVendor || product != id->idProduct) {
41537d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PROBE, "Bad vendor / product from device\n");
4166a7eba24SJean-Francois Moine 		return -EINVAL;
4176a7eba24SJean-Francois Moine 	}
4189d64fdb1SJean-Francois Moine 
4196a7eba24SJean-Francois Moine 	cam = &gspca_dev->cam;
420eb3fb7c9SHans de Goede 	cam->needs_full_bandwidth = 1;
4219d64fdb1SJean-Francois Moine 
4229d64fdb1SJean-Francois Moine 	sd->chip_revision = id->driver_info;
423b77c0046SJean-Francois Moine 	if (sd->chip_revision == Rev012A) {
424b77c0046SJean-Francois Moine 		cam->cam_mode = sif_012a_mode;
425b77c0046SJean-Francois Moine 		cam->nmodes = ARRAY_SIZE(sif_012a_mode);
426b77c0046SJean-Francois Moine 	} else {
427b77c0046SJean-Francois Moine 		cam->cam_mode = sif_072a_mode;
428b77c0046SJean-Francois Moine 		cam->nmodes = ARRAY_SIZE(sif_072a_mode);
429b77c0046SJean-Francois Moine 	}
430d698dc6bSJean-Francois Moine 	sd->expo12a = EXPO12A_DEF;
4316a7eba24SJean-Francois Moine 	return 0;
4326a7eba24SJean-Francois Moine }
4336a7eba24SJean-Francois Moine 
434012d6b02SJean-Francois Moine /* this function is called at probe and resume time */
435012d6b02SJean-Francois Moine static int sd_init_12a(struct gspca_dev *gspca_dev)
4366a7eba24SJean-Francois Moine {
43737d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 012a\n");
4386a7eba24SJean-Francois Moine 	init_161rev12A(gspca_dev);
4397879d459SJean-Francois Moine 	return 0;
4406a7eba24SJean-Francois Moine }
441012d6b02SJean-Francois Moine static int sd_init_72a(struct gspca_dev *gspca_dev)
4427879d459SJean-Francois Moine {
44337d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 072a\n");
4440dbc2c16SJean-Francois Moine 	write_vector(gspca_dev, rev72a_reset);
4450dbc2c16SJean-Francois Moine 	msleep(200);
446f8a04a6fSJean-Francois Moine 	write_vector(gspca_dev, rev72a_init_data1);
447f8a04a6fSJean-Francois Moine 	write_sensor_72a(gspca_dev, rev72a_init_sensor1);
448f8a04a6fSJean-Francois Moine 	write_vector(gspca_dev, rev72a_init_data2);
449f8a04a6fSJean-Francois Moine 	write_sensor_72a(gspca_dev, rev72a_init_sensor2);
450c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8112, 0x30);
4516a7eba24SJean-Francois Moine 	return 0;
4526a7eba24SJean-Francois Moine }
4536a7eba24SJean-Francois Moine 
4543fa24bf5SHans Verkuil static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
4553fa24bf5SHans Verkuil {
456a8931d59SHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
457a8931d59SHans de Goede 	__u16 reg;
4583fa24bf5SHans Verkuil 
459a8931d59SHans de Goede 	if (sd->chip_revision == Rev012A)
460a8931d59SHans de Goede 		reg = 0x8610;
461a8931d59SHans de Goede 	else
462a8931d59SHans de Goede 		reg = 0x8611;
463a8931d59SHans de Goede 
464c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, reg + 0, val);		/* R */
465c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, reg + 1, val);		/* Gr */
466c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, reg + 2, val);		/* B */
467c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, reg + 3, val);		/* Gb */
4683fa24bf5SHans Verkuil }
4693fa24bf5SHans Verkuil 
4703fa24bf5SHans Verkuil static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast)
4716a7eba24SJean-Francois Moine {
4726a7eba24SJean-Francois Moine 	struct sd *sd = (struct sd *) gspca_dev;
4735b7ed28eSJean-Francois Moine 	__u8 blue, red;
4745b7ed28eSJean-Francois Moine 	__u16 reg;
4756c9d3c59SJean-Francois Moine 
4766c9d3c59SJean-Francois Moine 	/* try to emulate MS-win as possible */
4775b7ed28eSJean-Francois Moine 	red = 0x20 + white * 3 / 8;
4785b7ed28eSJean-Francois Moine 	blue = 0x90 - white * 5 / 8;
4795b7ed28eSJean-Francois Moine 	if (sd->chip_revision == Rev012A) {
4805b7ed28eSJean-Francois Moine 		reg = 0x8614;
4815b7ed28eSJean-Francois Moine 	} else {
4825b7ed28eSJean-Francois Moine 		reg = 0x8651;
4833fa24bf5SHans Verkuil 		red += contrast - 0x20;
4843fa24bf5SHans Verkuil 		blue += contrast - 0x20;
485c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8652, contrast + 0x20); /* Gr */
486c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8654, contrast + 0x20); /* Gb */
4875b7ed28eSJean-Francois Moine 	}
488c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, reg, red);
489c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, reg + 2, blue);
4906c9d3c59SJean-Francois Moine }
4917879d459SJean-Francois Moine 
4927879d459SJean-Francois Moine /* rev 12a only */
4933fa24bf5SHans Verkuil static void setexposure(struct gspca_dev *gspca_dev, s32 val)
4947879d459SJean-Francois Moine {
495d0848eb2SHans de Goede 	int i, expo = 0;
4967879d459SJean-Francois Moine 
4970fc23d20SHans de Goede 	/* Register 0x8309 controls exposure for the spca561,
4980fc23d20SHans de Goede 	   the basic exposure setting goes from 1-2047, where 1 is completely
4990fc23d20SHans de Goede 	   dark and 2047 is very bright. It not only influences exposure but
5000fc23d20SHans de Goede 	   also the framerate (to allow for longer exposure) from 1 - 300 it
5010fc23d20SHans de Goede 	   only raises the exposure time then from 300 - 600 it halves the
5020fc23d20SHans de Goede 	   framerate to be able to further raise the exposure time and for every
5030fc23d20SHans de Goede 	   300 more it halves the framerate again. This allows for a maximum
5040fc23d20SHans de Goede 	   exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps).
5050fc23d20SHans de Goede 	   Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12
5060fc23d20SHans de Goede 	   configure a divider for the base framerate which us used at the
5070fc23d20SHans de Goede 	   exposure setting of 1-300. These bits configure the base framerate
5080fc23d20SHans de Goede 	   according to the following formula: fps = 60 / (value + 2) */
509d0848eb2SHans de Goede 
510d0848eb2SHans de Goede 	/* We choose to use the high bits setting the fixed framerate divisor
511d0848eb2SHans de Goede 	   asap, as setting high basic exposure setting without the fixed
512d0848eb2SHans de Goede 	   divider in combination with high gains makes the cam stop */
513d0848eb2SHans de Goede 	int table[] =  { 0, 450, 550, 625, EXPOSURE_MAX };
514d0848eb2SHans de Goede 
515d0848eb2SHans de Goede 	for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
5163fa24bf5SHans Verkuil 		if (val <= table[i + 1]) {
5173fa24bf5SHans Verkuil 			expo  = val - table[i];
518d0848eb2SHans de Goede 			if (i)
519d0848eb2SHans de Goede 				expo += 300;
520d0848eb2SHans de Goede 			expo |= i << 11;
521d0848eb2SHans de Goede 			break;
5220fc23d20SHans de Goede 		}
523d0848eb2SHans de Goede 	}
524d0848eb2SHans de Goede 
52535dc1b4cSJean-Francois Moine 	gspca_dev->usb_buf[0] = expo;
52635dc1b4cSJean-Francois Moine 	gspca_dev->usb_buf[1] = expo >> 8;
52735dc1b4cSJean-Francois Moine 	reg_w_buf(gspca_dev, 0x8309, 2);
5287879d459SJean-Francois Moine }
5297879d459SJean-Francois Moine 
5307879d459SJean-Francois Moine /* rev 12a only */
5313fa24bf5SHans Verkuil static void setgain(struct gspca_dev *gspca_dev, s32 val)
5327879d459SJean-Francois Moine {
533d0848eb2SHans de Goede 	/* gain reg low 6 bits  0-63 gain, bit 6 and 7, both double the
534d0848eb2SHans de Goede 	   sensitivity when set, so 31 + one of them set == 63, and 15
535d0848eb2SHans de Goede 	   with both of them set == 63 */
5363fa24bf5SHans Verkuil 	if (val < 64)
5373fa24bf5SHans Verkuil 		gspca_dev->usb_buf[0] = val;
5383fa24bf5SHans Verkuil 	else if (val < 128)
5393fa24bf5SHans Verkuil 		gspca_dev->usb_buf[0] = (val / 2) | 0x40;
540d0848eb2SHans de Goede 	else
5413fa24bf5SHans Verkuil 		gspca_dev->usb_buf[0] = (val / 4) | 0xc0;
542d0848eb2SHans de Goede 
54335dc1b4cSJean-Francois Moine 	gspca_dev->usb_buf[1] = 0;
54435dc1b4cSJean-Francois Moine 	reg_w_buf(gspca_dev, 0x8335, 2);
5456c9d3c59SJean-Francois Moine }
5466c9d3c59SJean-Francois Moine 
5473fa24bf5SHans Verkuil static void setautogain(struct gspca_dev *gspca_dev, s32 val)
548cebf3b67SJean-Francois Moine {
549cebf3b67SJean-Francois Moine 	struct sd *sd = (struct sd *) gspca_dev;
550cebf3b67SJean-Francois Moine 
5513fa24bf5SHans Verkuil 	if (val)
552cebf3b67SJean-Francois Moine 		sd->ag_cnt = AG_CNT_START;
553cebf3b67SJean-Francois Moine 	else
554cebf3b67SJean-Francois Moine 		sd->ag_cnt = -1;
555cebf3b67SJean-Francois Moine }
556cebf3b67SJean-Francois Moine 
55772ab97ceSJean-Francois Moine static int sd_start_12a(struct gspca_dev *gspca_dev)
5586a7eba24SJean-Francois Moine {
5596a7eba24SJean-Francois Moine 	int mode;
5605b7ed28eSJean-Francois Moine 	static const __u8 Reg8391[8] =
5615b7ed28eSJean-Francois Moine 		{0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
5626a7eba24SJean-Francois Moine 
563c2446b3eSJean-Francois Moine 	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
5646a7eba24SJean-Francois Moine 	if (mode <= 1) {
5656a7eba24SJean-Francois Moine 		/* Use compression on 320x240 and above */
566c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8500, 0x10 | mode);
5676a7eba24SJean-Francois Moine 	} else {
5686a7eba24SJean-Francois Moine 		/* I couldn't get the compression to work below 320x240
5696a7eba24SJean-Francois Moine 		 * Fortunately at these resolutions the bandwidth
5706a7eba24SJean-Francois Moine 		 * is sufficient to push raw frames at ~20fps */
571c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8500, mode);
5726a7eba24SJean-Francois Moine 	}		/* -- qq@kuku.eu.org */
57335dc1b4cSJean-Francois Moine 
5745b7ed28eSJean-Francois Moine 	gspca_dev->usb_buf[0] = 0xaa;
5755b7ed28eSJean-Francois Moine 	gspca_dev->usb_buf[1] = 0x00;
5765b7ed28eSJean-Francois Moine 	reg_w_buf(gspca_dev, 0x8307, 2);
5775b7ed28eSJean-Francois Moine 	/* clock - lower 0x8X values lead to fps > 30 */
578c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8700, 0x8a);
579739570bbSJean-Francois Moine 					/* 0x8f 0x85 0x27 clock */
580c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8112, 0x1e | 0x20);
581c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x850b, 0x03);
5825b7ed28eSJean-Francois Moine 	memcpy(gspca_dev->usb_buf, Reg8391, 8);
5835b7ed28eSJean-Francois Moine 	reg_w_buf(gspca_dev, 0x8391, 8);
5845b7ed28eSJean-Francois Moine 	reg_w_buf(gspca_dev, 0x8390, 8);
5856b33e5e7SHans de Goede 
5866b33e5e7SHans de Goede 	/* Led ON (bit 3 -> 0 */
587c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8114, 0x00);
58872ab97ceSJean-Francois Moine 	return 0;
5897879d459SJean-Francois Moine }
59072ab97ceSJean-Francois Moine static int sd_start_72a(struct gspca_dev *gspca_dev)
5917879d459SJean-Francois Moine {
5923fa24bf5SHans Verkuil 	struct sd *sd = (struct sd *) gspca_dev;
5937879d459SJean-Francois Moine 	int Clck;
5947879d459SJean-Francois Moine 	int mode;
5957879d459SJean-Francois Moine 
5960dbc2c16SJean-Francois Moine 	write_vector(gspca_dev, rev72a_reset);
5970dbc2c16SJean-Francois Moine 	msleep(200);
5980dbc2c16SJean-Francois Moine 	write_vector(gspca_dev, rev72a_init_data1);
5990dbc2c16SJean-Francois Moine 	write_sensor_72a(gspca_dev, rev72a_init_sensor1);
6000dbc2c16SJean-Francois Moine 
6017879d459SJean-Francois Moine 	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
6027879d459SJean-Francois Moine 	switch (mode) {
6037879d459SJean-Francois Moine 	default:
604a48196a2SJean-Francois Moine 	case 0:
605a48196a2SJean-Francois Moine 		Clck = 0x27;		/* ms-win 0x87 */
606a48196a2SJean-Francois Moine 		break;
607a48196a2SJean-Francois Moine 	case 1:
6087879d459SJean-Francois Moine 		Clck = 0x25;
6097879d459SJean-Francois Moine 		break;
6107879d459SJean-Francois Moine 	case 2:
6117879d459SJean-Francois Moine 		Clck = 0x22;
6127879d459SJean-Francois Moine 		break;
6137879d459SJean-Francois Moine 	case 3:
6147879d459SJean-Francois Moine 		Clck = 0x21;
6156a7eba24SJean-Francois Moine 		break;
6166a7eba24SJean-Francois Moine 	}
617c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8700, Clck);	/* 0x27 clock */
618c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8702, 0x81);
619c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8500, mode);	/* mode */
6200dbc2c16SJean-Francois Moine 	write_sensor_72a(gspca_dev, rev72a_init_sensor2);
6213fa24bf5SHans Verkuil 	setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue),
6223fa24bf5SHans Verkuil 			v4l2_ctrl_g_ctrl(sd->contrast));
6235b7ed28eSJean-Francois Moine /*	setbrightness(gspca_dev);	 * fixme: bad values */
6243fa24bf5SHans Verkuil 	setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
625c93396e1STheodore Kilgore 	reg_w_val(gspca_dev, 0x8112, 0x10 | 0x20);
62672ab97ceSJean-Francois Moine 	return 0;
6276a7eba24SJean-Francois Moine }
6286a7eba24SJean-Francois Moine 
6296a7eba24SJean-Francois Moine static void sd_stopN(struct gspca_dev *gspca_dev)
6306a7eba24SJean-Francois Moine {
631d698dc6bSJean-Francois Moine 	struct sd *sd = (struct sd *) gspca_dev;
632d698dc6bSJean-Francois Moine 
633d698dc6bSJean-Francois Moine 	if (sd->chip_revision == Rev012A) {
634c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8112, 0x0e);
6356b33e5e7SHans de Goede 		/* Led Off (bit 3 -> 1 */
636c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8114, 0x08);
637d698dc6bSJean-Francois Moine 	} else {
638c93396e1STheodore Kilgore 		reg_w_val(gspca_dev, 0x8112, 0x20);
639c93396e1STheodore Kilgore /*		reg_w_val(gspca_dev, 0x8102, 0x00); ?? */
640d698dc6bSJean-Francois Moine 	}
6416a7eba24SJean-Francois Moine }
6426a7eba24SJean-Francois Moine 
643cebf3b67SJean-Francois Moine static void do_autogain(struct gspca_dev *gspca_dev)
6446a7eba24SJean-Francois Moine {
6456a7eba24SJean-Francois Moine 	struct sd *sd = (struct sd *) gspca_dev;
646cebf3b67SJean-Francois Moine 	int expotimes;
647cebf3b67SJean-Francois Moine 	int pixelclk;
648cebf3b67SJean-Francois Moine 	int gainG;
6496a7eba24SJean-Francois Moine 	__u8 R, Gr, Gb, B;
6506a7eba24SJean-Francois Moine 	int y;
6516a7eba24SJean-Francois Moine 	__u8 luma_mean = 110;
6526a7eba24SJean-Francois Moine 	__u8 luma_delta = 20;
6536a7eba24SJean-Francois Moine 	__u8 spring = 4;
6546a7eba24SJean-Francois Moine 
655cebf3b67SJean-Francois Moine 	if (sd->ag_cnt < 0)
656cebf3b67SJean-Francois Moine 		return;
657cebf3b67SJean-Francois Moine 	if (--sd->ag_cnt >= 0)
658cebf3b67SJean-Francois Moine 		return;
659cebf3b67SJean-Francois Moine 	sd->ag_cnt = AG_CNT_START;
660cebf3b67SJean-Francois Moine 
6616a7eba24SJean-Francois Moine 	switch (sd->chip_revision) {
6626a7eba24SJean-Francois Moine 	case Rev072A:
663739570bbSJean-Francois Moine 		reg_r(gspca_dev, 0x8621, 1);
664739570bbSJean-Francois Moine 		Gr = gspca_dev->usb_buf[0];
665739570bbSJean-Francois Moine 		reg_r(gspca_dev, 0x8622, 1);
666739570bbSJean-Francois Moine 		R = gspca_dev->usb_buf[0];
667739570bbSJean-Francois Moine 		reg_r(gspca_dev, 0x8623, 1);
668739570bbSJean-Francois Moine 		B = gspca_dev->usb_buf[0];
669739570bbSJean-Francois Moine 		reg_r(gspca_dev, 0x8624, 1);
670739570bbSJean-Francois Moine 		Gb = gspca_dev->usb_buf[0];
6716a7eba24SJean-Francois Moine 		y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8;
6726a7eba24SJean-Francois Moine 		/* u= (128*B-(43*(Gr+Gb+R))) >> 8; */
6736a7eba24SJean-Francois Moine 		/* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */
6746a7eba24SJean-Francois Moine 
6756a7eba24SJean-Francois Moine 		if (y < luma_mean - luma_delta ||
6766a7eba24SJean-Francois Moine 		    y > luma_mean + luma_delta) {
6776a7eba24SJean-Francois Moine 			expotimes = i2c_read(gspca_dev, 0x09, 0x10);
6786a7eba24SJean-Francois Moine 			pixelclk = 0x0800;
6796a7eba24SJean-Francois Moine 			expotimes = expotimes & 0x07ff;
6806a7eba24SJean-Francois Moine 			gainG = i2c_read(gspca_dev, 0x35, 0x10);
6816a7eba24SJean-Francois Moine 
6826a7eba24SJean-Francois Moine 			expotimes += (luma_mean - y) >> spring;
6836a7eba24SJean-Francois Moine 			gainG += (luma_mean - y) / 50;
6846a7eba24SJean-Francois Moine 
6856a7eba24SJean-Francois Moine 			if (gainG > 0x3f)
6866a7eba24SJean-Francois Moine 				gainG = 0x3f;
68735dc1b4cSJean-Francois Moine 			else if (gainG < 3)
6886a7eba24SJean-Francois Moine 				gainG = 3;
6896a7eba24SJean-Francois Moine 			i2c_write(gspca_dev, gainG, 0x35);
6906a7eba24SJean-Francois Moine 
69135dc1b4cSJean-Francois Moine 			if (expotimes > 0x0256)
6926a7eba24SJean-Francois Moine 				expotimes = 0x0256;
69335dc1b4cSJean-Francois Moine 			else if (expotimes < 3)
6946a7eba24SJean-Francois Moine 				expotimes = 3;
6956a7eba24SJean-Francois Moine 			i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
6966a7eba24SJean-Francois Moine 		}
6976a7eba24SJean-Francois Moine 		break;
6986a7eba24SJean-Francois Moine 	}
6996a7eba24SJean-Francois Moine }
7006a7eba24SJean-Francois Moine 
7016a7eba24SJean-Francois Moine static void sd_pkt_scan(struct gspca_dev *gspca_dev,
70276dd272bSJean-Francois Moine 			u8 *data,		/* isoc packet */
7036a7eba24SJean-Francois Moine 			int len)		/* iso packet length */
7046a7eba24SJean-Francois Moine {
7050fc23d20SHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
7060fc23d20SHans de Goede 
707576ed7b5SJean-Francois Moine 	len--;
708576ed7b5SJean-Francois Moine 	switch (*data++) {			/* sequence number */
7096a7eba24SJean-Francois Moine 	case 0:					/* start of frame */
71076dd272bSJean-Francois Moine 		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
711436c2c53SHans de Goede 
712436c2c53SHans de Goede 		/* This should never happen */
713436c2c53SHans de Goede 		if (len < 2) {
71437d5efb0SJoe Perches 			gspca_err(gspca_dev, "Short SOF packet, ignoring\n\n\n\n\n");
715436c2c53SHans de Goede 			gspca_dev->last_packet_type = DISCARD_PACKET;
716436c2c53SHans de Goede 			return;
717436c2c53SHans de Goede 		}
718436c2c53SHans de Goede 
71960d21563SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT)
720436c2c53SHans de Goede 		if (data[0] & 0x20) {
721436c2c53SHans de Goede 			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
722436c2c53SHans de Goede 			input_sync(gspca_dev->input_dev);
723436c2c53SHans de Goede 			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
724436c2c53SHans de Goede 			input_sync(gspca_dev->input_dev);
725436c2c53SHans de Goede 		}
726436c2c53SHans de Goede #endif
727436c2c53SHans de Goede 
7286a7eba24SJean-Francois Moine 		if (data[1] & 0x10) {
7296a7eba24SJean-Francois Moine 			/* compressed bayer */
73076dd272bSJean-Francois Moine 			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
7316a7eba24SJean-Francois Moine 		} else {
73254ab92caSHans de Goede 			/* raw bayer (with a header, which we skip) */
7330fc23d20SHans de Goede 			if (sd->chip_revision == Rev012A) {
7340fc23d20SHans de Goede 				data += 20;
7350fc23d20SHans de Goede 				len -= 20;
7360fc23d20SHans de Goede 			} else {
737b77c0046SJean-Francois Moine 				data += 16;
738b77c0046SJean-Francois Moine 				len -= 16;
7390fc23d20SHans de Goede 			}
74076dd272bSJean-Francois Moine 			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
7416a7eba24SJean-Francois Moine 		}
7426a7eba24SJean-Francois Moine 		return;
74335dc1b4cSJean-Francois Moine 	case 0xff:			/* drop (empty mpackets) */
7446a7eba24SJean-Francois Moine 		return;
7456a7eba24SJean-Francois Moine 	}
74676dd272bSJean-Francois Moine 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
7476a7eba24SJean-Francois Moine }
7486a7eba24SJean-Francois Moine 
7493fa24bf5SHans Verkuil static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
7506a7eba24SJean-Francois Moine {
7513fa24bf5SHans Verkuil 	struct gspca_dev *gspca_dev =
7523fa24bf5SHans Verkuil 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
7536a7eba24SJean-Francois Moine 	struct sd *sd = (struct sd *)gspca_dev;
7546a7eba24SJean-Francois Moine 
7553fa24bf5SHans Verkuil 	gspca_dev->usb_err = 0;
7563fa24bf5SHans Verkuil 
7573fa24bf5SHans Verkuil 	if (!gspca_dev->streaming)
7586a7eba24SJean-Francois Moine 		return 0;
7593fa24bf5SHans Verkuil 
7603fa24bf5SHans Verkuil 	switch (ctrl->id) {
7613fa24bf5SHans Verkuil 	case V4L2_CID_BRIGHTNESS:
7623fa24bf5SHans Verkuil 		setbrightness(gspca_dev, ctrl->val);
7633fa24bf5SHans Verkuil 		break;
7643fa24bf5SHans Verkuil 	case V4L2_CID_CONTRAST:
7653fa24bf5SHans Verkuil 		/* hue/contrast control cluster for 72a */
7663fa24bf5SHans Verkuil 		setwhite(gspca_dev, sd->hue->val, ctrl->val);
7673fa24bf5SHans Verkuil 		break;
7683fa24bf5SHans Verkuil 	case V4L2_CID_HUE:
7693fa24bf5SHans Verkuil 		/* just plain hue control for 12a */
7703fa24bf5SHans Verkuil 		setwhite(gspca_dev, ctrl->val, 0);
7713fa24bf5SHans Verkuil 		break;
7723fa24bf5SHans Verkuil 	case V4L2_CID_EXPOSURE:
7733fa24bf5SHans Verkuil 		setexposure(gspca_dev, ctrl->val);
7743fa24bf5SHans Verkuil 		break;
7753fa24bf5SHans Verkuil 	case V4L2_CID_GAIN:
7763fa24bf5SHans Verkuil 		setgain(gspca_dev, ctrl->val);
7773fa24bf5SHans Verkuil 		break;
7783fa24bf5SHans Verkuil 	case V4L2_CID_AUTOGAIN:
7793fa24bf5SHans Verkuil 		setautogain(gspca_dev, ctrl->val);
7803fa24bf5SHans Verkuil 		break;
7813fa24bf5SHans Verkuil 	}
7823fa24bf5SHans Verkuil 	return gspca_dev->usb_err;
7836a7eba24SJean-Francois Moine }
7846a7eba24SJean-Francois Moine 
7853fa24bf5SHans Verkuil static const struct v4l2_ctrl_ops sd_ctrl_ops = {
7863fa24bf5SHans Verkuil 	.s_ctrl = sd_s_ctrl,
7877879d459SJean-Francois Moine };
7887879d459SJean-Francois Moine 
7893fa24bf5SHans Verkuil static int sd_init_controls_12a(struct gspca_dev *gspca_dev)
7907879d459SJean-Francois Moine {
7913fa24bf5SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
7923fa24bf5SHans Verkuil 
7933fa24bf5SHans Verkuil 	gspca_dev->vdev.ctrl_handler = hdl;
7943fa24bf5SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 3);
7953fa24bf5SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
7963fa24bf5SHans Verkuil 			V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
7973fa24bf5SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
798a8931d59SHans de Goede 			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
799a8931d59SHans de Goede 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
8003fa24bf5SHans Verkuil 			V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700);
8013fa24bf5SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
8023fa24bf5SHans Verkuil 			V4L2_CID_GAIN, 0, 255, 1, 63);
8033fa24bf5SHans Verkuil 
8043fa24bf5SHans Verkuil 	if (hdl->error) {
8053fa24bf5SHans Verkuil 		pr_err("Could not initialize controls\n");
8063fa24bf5SHans Verkuil 		return hdl->error;
8073fa24bf5SHans Verkuil 	}
8083fa24bf5SHans Verkuil 	return 0;
8093fa24bf5SHans Verkuil }
8103fa24bf5SHans Verkuil 
8113fa24bf5SHans Verkuil static int sd_init_controls_72a(struct gspca_dev *gspca_dev)
8127879d459SJean-Francois Moine {
8133fa24bf5SHans Verkuil 	struct sd *sd = (struct sd *)gspca_dev;
8143fa24bf5SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
8153fa24bf5SHans Verkuil 
8163fa24bf5SHans Verkuil 	gspca_dev->vdev.ctrl_handler = hdl;
8173fa24bf5SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 4);
8183fa24bf5SHans Verkuil 	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
8193fa24bf5SHans Verkuil 			V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20);
8203fa24bf5SHans Verkuil 	sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
8213fa24bf5SHans Verkuil 			V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
8223fa24bf5SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
8233fa24bf5SHans Verkuil 			V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20);
8243fa24bf5SHans Verkuil 	sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
8253fa24bf5SHans Verkuil 			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
8263fa24bf5SHans Verkuil 
8273fa24bf5SHans Verkuil 	if (hdl->error) {
8283fa24bf5SHans Verkuil 		pr_err("Could not initialize controls\n");
8293fa24bf5SHans Verkuil 		return hdl->error;
8303fa24bf5SHans Verkuil 	}
8313fa24bf5SHans Verkuil 	v4l2_ctrl_cluster(2, &sd->contrast);
8323fa24bf5SHans Verkuil 	return 0;
8333fa24bf5SHans Verkuil }
8347879d459SJean-Francois Moine 
8356a7eba24SJean-Francois Moine /* sub-driver description */
8367879d459SJean-Francois Moine static const struct sd_desc sd_desc_12a = {
8376a7eba24SJean-Francois Moine 	.name = MODULE_NAME,
8383fa24bf5SHans Verkuil 	.init_controls = sd_init_controls_12a,
8396a7eba24SJean-Francois Moine 	.config = sd_config,
840012d6b02SJean-Francois Moine 	.init = sd_init_12a,
8417879d459SJean-Francois Moine 	.start = sd_start_12a,
8427879d459SJean-Francois Moine 	.stopN = sd_stopN,
8437879d459SJean-Francois Moine 	.pkt_scan = sd_pkt_scan,
84460d21563SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT)
845436c2c53SHans de Goede 	.other_input = 1,
846436c2c53SHans de Goede #endif
8477879d459SJean-Francois Moine };
8487879d459SJean-Francois Moine static const struct sd_desc sd_desc_72a = {
8497879d459SJean-Francois Moine 	.name = MODULE_NAME,
8503fa24bf5SHans Verkuil 	.init_controls = sd_init_controls_72a,
8517879d459SJean-Francois Moine 	.config = sd_config,
852012d6b02SJean-Francois Moine 	.init = sd_init_72a,
8537879d459SJean-Francois Moine 	.start = sd_start_72a,
8546a7eba24SJean-Francois Moine 	.stopN = sd_stopN,
8556a7eba24SJean-Francois Moine 	.pkt_scan = sd_pkt_scan,
856cebf3b67SJean-Francois Moine 	.dq_callback = do_autogain,
85760d21563SPeter Senna Tschudin #if IS_ENABLED(CONFIG_INPUT)
858436c2c53SHans de Goede 	.other_input = 1,
859436c2c53SHans de Goede #endif
8606a7eba24SJean-Francois Moine };
8617879d459SJean-Francois Moine static const struct sd_desc *sd_desc[2] = {
8627879d459SJean-Francois Moine 	&sd_desc_12a,
8637879d459SJean-Francois Moine 	&sd_desc_72a
8647879d459SJean-Francois Moine };
8656a7eba24SJean-Francois Moine 
8666a7eba24SJean-Francois Moine /* -- module initialisation -- */
86795c967c1SJean-François Moine static const struct usb_device_id device_table[] = {
86887581aa5SJean-Francois Moine 	{USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A},
86987581aa5SJean-Francois Moine 	{USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A},
87087581aa5SJean-Francois Moine 	{USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A},
871f8f73d01SJohn Ellson 	{USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A},
87287581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A},
87387581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A},
87487581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A},
87587581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A},
87687581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A},
87787581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A},
87887581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A},
87987581aa5SJean-Francois Moine 	{USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A},
88087581aa5SJean-Francois Moine 	{USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A},
88187581aa5SJean-Francois Moine 	{USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A},
88287581aa5SJean-Francois Moine 	{USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A},
88387581aa5SJean-Francois Moine 	{USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A},
8846a7eba24SJean-Francois Moine 	{}
8856a7eba24SJean-Francois Moine };
8866a7eba24SJean-Francois Moine 
8876a7eba24SJean-Francois Moine MODULE_DEVICE_TABLE(usb, device_table);
8886a7eba24SJean-Francois Moine 
8896a7eba24SJean-Francois Moine /* -- device connect -- */
8906a7eba24SJean-Francois Moine static int sd_probe(struct usb_interface *intf,
8916a7eba24SJean-Francois Moine 		    const struct usb_device_id *id)
8926a7eba24SJean-Francois Moine {
8937879d459SJean-Francois Moine 	return gspca_dev_probe(intf, id,
8947879d459SJean-Francois Moine 				sd_desc[id->driver_info],
8957879d459SJean-Francois Moine 				sizeof(struct sd),
8966a7eba24SJean-Francois Moine 			       THIS_MODULE);
8976a7eba24SJean-Francois Moine }
8986a7eba24SJean-Francois Moine 
8996a7eba24SJean-Francois Moine static struct usb_driver sd_driver = {
9006a7eba24SJean-Francois Moine 	.name = MODULE_NAME,
9016a7eba24SJean-Francois Moine 	.id_table = device_table,
9026a7eba24SJean-Francois Moine 	.probe = sd_probe,
9036a7eba24SJean-Francois Moine 	.disconnect = gspca_disconnect,
9046a709749SJean-Francois Moine #ifdef CONFIG_PM
9056a709749SJean-Francois Moine 	.suspend = gspca_suspend,
9066a709749SJean-Francois Moine 	.resume = gspca_resume,
9078bb58964SHans de Goede 	.reset_resume = gspca_resume,
9086a709749SJean-Francois Moine #endif
9096a7eba24SJean-Francois Moine };
9106a7eba24SJean-Francois Moine 
911ecb3b2b3SGreg Kroah-Hartman module_usb_driver(sd_driver);
912