162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Sunplus spca561 subdriver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define MODULE_NAME "spca561"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/input.h>
1562306a36Sopenharmony_ci#include "gspca.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciMODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
1862306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
1962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define EXPOSURE_MAX (2047 + 325)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* specific webcam descriptor */
2462306a36Sopenharmony_cistruct sd {
2562306a36Sopenharmony_ci	struct gspca_dev gspca_dev;	/* !! must be the first item */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	struct { /* hue/contrast control cluster */
2862306a36Sopenharmony_ci		struct v4l2_ctrl *contrast;
2962306a36Sopenharmony_ci		struct v4l2_ctrl *hue;
3062306a36Sopenharmony_ci	};
3162306a36Sopenharmony_ci	struct v4l2_ctrl *autogain;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define EXPO12A_DEF 3
3462306a36Sopenharmony_ci	__u8 expo12a;		/* expo/gain? for rev 12a */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	__u8 chip_revision;
3762306a36Sopenharmony_ci#define Rev012A 0
3862306a36Sopenharmony_ci#define Rev072A 1
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	signed char ag_cnt;
4162306a36Sopenharmony_ci#define AG_CNT_START 13
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct v4l2_pix_format sif_012a_mode[] = {
4562306a36Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
4662306a36Sopenharmony_ci		.bytesperline = 160,
4762306a36Sopenharmony_ci		.sizeimage = 160 * 120,
4862306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
4962306a36Sopenharmony_ci		.priv = 3},
5062306a36Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
5162306a36Sopenharmony_ci		.bytesperline = 176,
5262306a36Sopenharmony_ci		.sizeimage = 176 * 144,
5362306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
5462306a36Sopenharmony_ci		.priv = 2},
5562306a36Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
5662306a36Sopenharmony_ci		.bytesperline = 320,
5762306a36Sopenharmony_ci		.sizeimage = 320 * 240 * 4 / 8,
5862306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
5962306a36Sopenharmony_ci		.priv = 1},
6062306a36Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
6162306a36Sopenharmony_ci		.bytesperline = 352,
6262306a36Sopenharmony_ci		.sizeimage = 352 * 288 * 4 / 8,
6362306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
6462306a36Sopenharmony_ci		.priv = 0},
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const struct v4l2_pix_format sif_072a_mode[] = {
6862306a36Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
6962306a36Sopenharmony_ci		.bytesperline = 160,
7062306a36Sopenharmony_ci		.sizeimage = 160 * 120,
7162306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
7262306a36Sopenharmony_ci		.priv = 3},
7362306a36Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
7462306a36Sopenharmony_ci		.bytesperline = 176,
7562306a36Sopenharmony_ci		.sizeimage = 176 * 144,
7662306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
7762306a36Sopenharmony_ci		.priv = 2},
7862306a36Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
7962306a36Sopenharmony_ci		.bytesperline = 320,
8062306a36Sopenharmony_ci		.sizeimage = 320 * 240,
8162306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
8262306a36Sopenharmony_ci		.priv = 1},
8362306a36Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
8462306a36Sopenharmony_ci		.bytesperline = 352,
8562306a36Sopenharmony_ci		.sizeimage = 352 * 288,
8662306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
8762306a36Sopenharmony_ci		.priv = 0},
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci * Initialization data
9262306a36Sopenharmony_ci * I'm not very sure how to split initialization from open data
9362306a36Sopenharmony_ci * chunks. For now, we'll consider everything as initialization
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_ci/* Frame packet header offsets for the spca561 */
9662306a36Sopenharmony_ci#define SPCA561_OFFSET_SNAP 1
9762306a36Sopenharmony_ci#define SPCA561_OFFSET_TYPE 2
9862306a36Sopenharmony_ci#define SPCA561_OFFSET_COMPRESS 3
9962306a36Sopenharmony_ci#define SPCA561_OFFSET_FRAMSEQ   4
10062306a36Sopenharmony_ci#define SPCA561_OFFSET_GPIO 5
10162306a36Sopenharmony_ci#define SPCA561_OFFSET_USBBUFF 6
10262306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN2GRAVE 7
10362306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN2RAVE 8
10462306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN2BAVE 9
10562306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN2GBAVE 10
10662306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN1GRAVE 11
10762306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN1RAVE 12
10862306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN1BAVE 13
10962306a36Sopenharmony_ci#define SPCA561_OFFSET_WIN1GBAVE 14
11062306a36Sopenharmony_ci#define SPCA561_OFFSET_FREQ 15
11162306a36Sopenharmony_ci#define SPCA561_OFFSET_VSYNC 16
11262306a36Sopenharmony_ci#define SPCA561_INDEX_I2C_BASE 0x8800
11362306a36Sopenharmony_ci#define SPCA561_SNAPBIT 0x20
11462306a36Sopenharmony_ci#define SPCA561_SNAPCTRL 0x40
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const u16 rev72a_reset[][2] = {
11762306a36Sopenharmony_ci	{0x0000, 0x8114},	/* Software GPIO output data */
11862306a36Sopenharmony_ci	{0x0001, 0x8114},	/* Software GPIO output data */
11962306a36Sopenharmony_ci	{0x0000, 0x8112},	/* Some kind of reset */
12062306a36Sopenharmony_ci	{}
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_cistatic const __u16 rev72a_init_data1[][2] = {
12362306a36Sopenharmony_ci	{0x0003, 0x8701},	/* PCLK clock delay adjustment */
12462306a36Sopenharmony_ci	{0x0001, 0x8703},	/* HSYNC from cmos inverted */
12562306a36Sopenharmony_ci	{0x0011, 0x8118},	/* Enable and conf sensor */
12662306a36Sopenharmony_ci	{0x0001, 0x8118},	/* Conf sensor */
12762306a36Sopenharmony_ci	{0x0092, 0x8804},	/* I know nothing about these */
12862306a36Sopenharmony_ci	{0x0010, 0x8802},	/* 0x88xx registers, so I won't */
12962306a36Sopenharmony_ci	{}
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_cistatic const u16 rev72a_init_sensor1[][2] = {
13262306a36Sopenharmony_ci	{0x0001, 0x000d},
13362306a36Sopenharmony_ci	{0x0002, 0x0018},
13462306a36Sopenharmony_ci	{0x0004, 0x0165},
13562306a36Sopenharmony_ci	{0x0005, 0x0021},
13662306a36Sopenharmony_ci	{0x0007, 0x00aa},
13762306a36Sopenharmony_ci	{0x0020, 0x1504},
13862306a36Sopenharmony_ci	{0x0039, 0x0002},
13962306a36Sopenharmony_ci	{0x0035, 0x0010},
14062306a36Sopenharmony_ci	{0x0009, 0x1049},
14162306a36Sopenharmony_ci	{0x0028, 0x000b},
14262306a36Sopenharmony_ci	{0x003b, 0x000f},
14362306a36Sopenharmony_ci	{0x003c, 0x0000},
14462306a36Sopenharmony_ci	{}
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_cistatic const __u16 rev72a_init_data2[][2] = {
14762306a36Sopenharmony_ci	{0x0018, 0x8601},	/* Pixel/line selection for color separation */
14862306a36Sopenharmony_ci	{0x0000, 0x8602},	/* Optical black level for user setting */
14962306a36Sopenharmony_ci	{0x0060, 0x8604},	/* Optical black horizontal offset */
15062306a36Sopenharmony_ci	{0x0002, 0x8605},	/* Optical black vertical offset */
15162306a36Sopenharmony_ci	{0x0000, 0x8603},	/* Non-automatic optical black level */
15262306a36Sopenharmony_ci	{0x0002, 0x865b},	/* Horizontal offset for valid pixels */
15362306a36Sopenharmony_ci	{0x0000, 0x865f},	/* Vertical valid pixels window (x2) */
15462306a36Sopenharmony_ci	{0x00b0, 0x865d},	/* Horizontal valid pixels window (x2) */
15562306a36Sopenharmony_ci	{0x0090, 0x865e},	/* Vertical valid lines window (x2) */
15662306a36Sopenharmony_ci	{0x00e0, 0x8406},	/* Memory buffer threshold */
15762306a36Sopenharmony_ci	{0x0000, 0x8660},	/* Compensation memory stuff */
15862306a36Sopenharmony_ci	{0x0002, 0x8201},	/* Output address for r/w serial EEPROM */
15962306a36Sopenharmony_ci	{0x0008, 0x8200},	/* Clear valid bit for serial EEPROM */
16062306a36Sopenharmony_ci	{0x0001, 0x8200},	/* OprMode to be executed by hardware */
16162306a36Sopenharmony_ci/* from ms-win */
16262306a36Sopenharmony_ci	{0x0000, 0x8611},	/* R offset for white balance */
16362306a36Sopenharmony_ci	{0x00fd, 0x8612},	/* Gr offset for white balance */
16462306a36Sopenharmony_ci	{0x0003, 0x8613},	/* B offset for white balance */
16562306a36Sopenharmony_ci	{0x0000, 0x8614},	/* Gb offset for white balance */
16662306a36Sopenharmony_ci/* from ms-win */
16762306a36Sopenharmony_ci	{0x0035, 0x8651},	/* R gain for white balance */
16862306a36Sopenharmony_ci	{0x0040, 0x8652},	/* Gr gain for white balance */
16962306a36Sopenharmony_ci	{0x005f, 0x8653},	/* B gain for white balance */
17062306a36Sopenharmony_ci	{0x0040, 0x8654},	/* Gb gain for white balance */
17162306a36Sopenharmony_ci	{0x0002, 0x8502},	/* Maximum average bit rate stuff */
17262306a36Sopenharmony_ci	{0x0011, 0x8802},
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	{0x0087, 0x8700},	/* Set master clock (96Mhz????) */
17562306a36Sopenharmony_ci	{0x0081, 0x8702},	/* Master clock output enable */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	{0x0000, 0x8500},	/* Set image type (352x288 no compression) */
17862306a36Sopenharmony_ci	/* Originally was 0x0010 (352x288 compression) */
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	{0x0002, 0x865b},	/* Horizontal offset for valid pixels */
18162306a36Sopenharmony_ci	{0x0003, 0x865c},	/* Vertical offset for valid lines */
18262306a36Sopenharmony_ci	{}
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_cistatic const u16 rev72a_init_sensor2[][2] = {
18562306a36Sopenharmony_ci	{0x0003, 0x0121},
18662306a36Sopenharmony_ci	{0x0004, 0x0165},
18762306a36Sopenharmony_ci	{0x0005, 0x002f},	/* blanking control column */
18862306a36Sopenharmony_ci	{0x0006, 0x0000},	/* blanking mode row*/
18962306a36Sopenharmony_ci	{0x000a, 0x0002},
19062306a36Sopenharmony_ci	{0x0009, 0x1061},	/* setexposure times && pixel clock
19162306a36Sopenharmony_ci				 * 0001 0 | 000 0110 0001 */
19262306a36Sopenharmony_ci	{0x0035, 0x0014},
19362306a36Sopenharmony_ci	{}
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/******************** QC Express etch2 stuff ********************/
19762306a36Sopenharmony_cistatic const __u16 Pb100_1map8300[][2] = {
19862306a36Sopenharmony_ci	/* reg, value */
19962306a36Sopenharmony_ci	{0x8320, 0x3304},
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	{0x8303, 0x0125},	/* image area */
20262306a36Sopenharmony_ci	{0x8304, 0x0169},
20362306a36Sopenharmony_ci	{0x8328, 0x000b},
20462306a36Sopenharmony_ci	{0x833c, 0x0001},		/*fixme: win:07*/
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	{0x832f, 0x1904},		/*fixme: was 0419*/
20762306a36Sopenharmony_ci	{0x8307, 0x00aa},
20862306a36Sopenharmony_ci	{0x8301, 0x0003},
20962306a36Sopenharmony_ci	{0x8302, 0x000e},
21062306a36Sopenharmony_ci	{}
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_cistatic const __u16 Pb100_2map8300[][2] = {
21362306a36Sopenharmony_ci	/* reg, value */
21462306a36Sopenharmony_ci	{0x8339, 0x0000},
21562306a36Sopenharmony_ci	{0x8307, 0x00aa},
21662306a36Sopenharmony_ci	{}
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic const __u16 spca561_161rev12A_data1[][2] = {
22062306a36Sopenharmony_ci	{0x29, 0x8118},		/* Control register (various enable bits) */
22162306a36Sopenharmony_ci	{0x08, 0x8114},		/* GPIO: Led off */
22262306a36Sopenharmony_ci	{0x0e, 0x8112},		/* 0x0e stream off 0x3e stream on */
22362306a36Sopenharmony_ci	{0x00, 0x8102},		/* white balance - new */
22462306a36Sopenharmony_ci	{0x92, 0x8804},
22562306a36Sopenharmony_ci	{0x04, 0x8802},		/* windows uses 08 */
22662306a36Sopenharmony_ci	{}
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_cistatic const __u16 spca561_161rev12A_data2[][2] = {
22962306a36Sopenharmony_ci	{0x21, 0x8118},
23062306a36Sopenharmony_ci	{0x10, 0x8500},
23162306a36Sopenharmony_ci	{0x07, 0x8601},
23262306a36Sopenharmony_ci	{0x07, 0x8602},
23362306a36Sopenharmony_ci	{0x04, 0x8501},
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	{0x07, 0x8201},		/* windows uses 02 */
23662306a36Sopenharmony_ci	{0x08, 0x8200},
23762306a36Sopenharmony_ci	{0x01, 0x8200},
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	{0x90, 0x8604},
24062306a36Sopenharmony_ci	{0x00, 0x8605},
24162306a36Sopenharmony_ci	{0xb0, 0x8603},
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* sensor gains */
24462306a36Sopenharmony_ci	{0x07, 0x8601},		/* white balance - new */
24562306a36Sopenharmony_ci	{0x07, 0x8602},		/* white balance - new */
24662306a36Sopenharmony_ci	{0x00, 0x8610},		/* *red */
24762306a36Sopenharmony_ci	{0x00, 0x8611},		/* 3f   *green */
24862306a36Sopenharmony_ci	{0x00, 0x8612},		/* green *blue */
24962306a36Sopenharmony_ci	{0x00, 0x8613},		/* blue *green */
25062306a36Sopenharmony_ci	{0x43, 0x8614},		/* green *red - white balance - was 0x35 */
25162306a36Sopenharmony_ci	{0x40, 0x8615},		/* 40   *green - white balance - was 0x35 */
25262306a36Sopenharmony_ci	{0x71, 0x8616},		/* 7a   *blue - white balance - was 0x35 */
25362306a36Sopenharmony_ci	{0x40, 0x8617},		/* 40   *green - white balance - was 0x35 */
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	{0x0c, 0x8620},		/* 0c */
25662306a36Sopenharmony_ci	{0xc8, 0x8631},		/* c8 */
25762306a36Sopenharmony_ci	{0xc8, 0x8634},		/* c8 */
25862306a36Sopenharmony_ci	{0x23, 0x8635},		/* 23 */
25962306a36Sopenharmony_ci	{0x1f, 0x8636},		/* 1f */
26062306a36Sopenharmony_ci	{0xdd, 0x8637},		/* dd */
26162306a36Sopenharmony_ci	{0xe1, 0x8638},		/* e1 */
26262306a36Sopenharmony_ci	{0x1d, 0x8639},		/* 1d */
26362306a36Sopenharmony_ci	{0x21, 0x863a},		/* 21 */
26462306a36Sopenharmony_ci	{0xe3, 0x863b},		/* e3 */
26562306a36Sopenharmony_ci	{0xdf, 0x863c},		/* df */
26662306a36Sopenharmony_ci	{0xf0, 0x8505},
26762306a36Sopenharmony_ci	{0x32, 0x850a},
26862306a36Sopenharmony_ci/*	{0x99, 0x8700},		 * - white balance - new (removed) */
26962306a36Sopenharmony_ci	/* HDG we used to do this in stop0, making the init state and the state
27062306a36Sopenharmony_ci	   after a start / stop different, so do this here instead. */
27162306a36Sopenharmony_ci	{0x29, 0x8118},
27262306a36Sopenharmony_ci	{}
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void reg_w_val(struct gspca_dev *gspca_dev, __u16 index, __u8 value)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	int ret;
27862306a36Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
28162306a36Sopenharmony_ci			      0,		/* request */
28262306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
28362306a36Sopenharmony_ci			      value, index, NULL, 0, 500);
28462306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "reg write: 0x%02x:0x%02x\n",
28562306a36Sopenharmony_ci		  index, value);
28662306a36Sopenharmony_ci	if (ret < 0)
28762306a36Sopenharmony_ci		pr_err("reg write: error %d\n", ret);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void write_vector(struct gspca_dev *gspca_dev,
29162306a36Sopenharmony_ci			const __u16 data[][2])
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	int i;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	i = 0;
29662306a36Sopenharmony_ci	while (data[i][1] != 0) {
29762306a36Sopenharmony_ci		reg_w_val(gspca_dev, data[i][1], data[i][0]);
29862306a36Sopenharmony_ci		i++;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/* read 'len' bytes to gspca_dev->usb_buf */
30362306a36Sopenharmony_cistatic void reg_r(struct gspca_dev *gspca_dev,
30462306a36Sopenharmony_ci		  __u16 index, __u16 length)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	usb_control_msg(gspca_dev->dev,
30762306a36Sopenharmony_ci			usb_rcvctrlpipe(gspca_dev->dev, 0),
30862306a36Sopenharmony_ci			0,			/* request */
30962306a36Sopenharmony_ci			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
31062306a36Sopenharmony_ci			0,			/* value */
31162306a36Sopenharmony_ci			index, gspca_dev->usb_buf, length, 500);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/* write 'len' bytes from gspca_dev->usb_buf */
31562306a36Sopenharmony_cistatic void reg_w_buf(struct gspca_dev *gspca_dev,
31662306a36Sopenharmony_ci		      __u16 index, __u16 len)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	usb_control_msg(gspca_dev->dev,
31962306a36Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
32062306a36Sopenharmony_ci			0,			/* request */
32162306a36Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
32262306a36Sopenharmony_ci			0,			/* value */
32362306a36Sopenharmony_ci			index, gspca_dev->usb_buf, len, 500);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	int retry = 60;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8801, reg);
33162306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8805, value);
33262306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8800, value >> 8);
33362306a36Sopenharmony_ci	do {
33462306a36Sopenharmony_ci		reg_r(gspca_dev, 0x8803, 1);
33562306a36Sopenharmony_ci		if (!gspca_dev->usb_buf[0])
33662306a36Sopenharmony_ci			return;
33762306a36Sopenharmony_ci		msleep(10);
33862306a36Sopenharmony_ci	} while (--retry);
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	int retry = 60;
34462306a36Sopenharmony_ci	__u8 value;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8804, 0x92);
34762306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8801, reg);
34862306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8802, mode | 0x01);
34962306a36Sopenharmony_ci	do {
35062306a36Sopenharmony_ci		reg_r(gspca_dev, 0x8803, 1);
35162306a36Sopenharmony_ci		if (!gspca_dev->usb_buf[0]) {
35262306a36Sopenharmony_ci			reg_r(gspca_dev, 0x8800, 1);
35362306a36Sopenharmony_ci			value = gspca_dev->usb_buf[0];
35462306a36Sopenharmony_ci			reg_r(gspca_dev, 0x8805, 1);
35562306a36Sopenharmony_ci			return ((int) value << 8) | gspca_dev->usb_buf[0];
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci		msleep(10);
35862306a36Sopenharmony_ci	} while (--retry);
35962306a36Sopenharmony_ci	return -1;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void sensor_mapwrite(struct gspca_dev *gspca_dev,
36362306a36Sopenharmony_ci			    const __u16 (*sensormap)[2])
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	while ((*sensormap)[0]) {
36662306a36Sopenharmony_ci		gspca_dev->usb_buf[0] = (*sensormap)[1];
36762306a36Sopenharmony_ci		gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
36862306a36Sopenharmony_ci		reg_w_buf(gspca_dev, (*sensormap)[0], 2);
36962306a36Sopenharmony_ci		sensormap++;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void write_sensor_72a(struct gspca_dev *gspca_dev,
37462306a36Sopenharmony_ci			    const __u16 (*sensor)[2])
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	while ((*sensor)[0]) {
37762306a36Sopenharmony_ci		i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
37862306a36Sopenharmony_ci		sensor++;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void init_161rev12A(struct gspca_dev *gspca_dev)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	write_vector(gspca_dev, spca561_161rev12A_data1);
38562306a36Sopenharmony_ci	sensor_mapwrite(gspca_dev, Pb100_1map8300);
38662306a36Sopenharmony_ci/*fixme: should be in sd_start*/
38762306a36Sopenharmony_ci	write_vector(gspca_dev, spca561_161rev12A_data2);
38862306a36Sopenharmony_ci	sensor_mapwrite(gspca_dev, Pb100_2map8300);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/* this function is called at probe time */
39262306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
39362306a36Sopenharmony_ci		     const struct usb_device_id *id)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
39662306a36Sopenharmony_ci	struct cam *cam;
39762306a36Sopenharmony_ci	__u16 vendor, product;
39862306a36Sopenharmony_ci	__u8 data1, data2;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* Read frm global register the USB product and vendor IDs, just to
40162306a36Sopenharmony_ci	 * prove that we can communicate with the device.  This works, which
40262306a36Sopenharmony_ci	 * confirms at we are communicating properly and that the device
40362306a36Sopenharmony_ci	 * is a 561. */
40462306a36Sopenharmony_ci	reg_r(gspca_dev, 0x8104, 1);
40562306a36Sopenharmony_ci	data1 = gspca_dev->usb_buf[0];
40662306a36Sopenharmony_ci	reg_r(gspca_dev, 0x8105, 1);
40762306a36Sopenharmony_ci	data2 = gspca_dev->usb_buf[0];
40862306a36Sopenharmony_ci	vendor = (data2 << 8) | data1;
40962306a36Sopenharmony_ci	reg_r(gspca_dev, 0x8106, 1);
41062306a36Sopenharmony_ci	data1 = gspca_dev->usb_buf[0];
41162306a36Sopenharmony_ci	reg_r(gspca_dev, 0x8107, 1);
41262306a36Sopenharmony_ci	data2 = gspca_dev->usb_buf[0];
41362306a36Sopenharmony_ci	product = (data2 << 8) | data1;
41462306a36Sopenharmony_ci	if (vendor != id->idVendor || product != id->idProduct) {
41562306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "Bad vendor / product from device\n");
41662306a36Sopenharmony_ci		return -EINVAL;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	cam = &gspca_dev->cam;
42062306a36Sopenharmony_ci	cam->needs_full_bandwidth = 1;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	sd->chip_revision = id->driver_info;
42362306a36Sopenharmony_ci	if (sd->chip_revision == Rev012A) {
42462306a36Sopenharmony_ci		cam->cam_mode = sif_012a_mode;
42562306a36Sopenharmony_ci		cam->nmodes = ARRAY_SIZE(sif_012a_mode);
42662306a36Sopenharmony_ci	} else {
42762306a36Sopenharmony_ci		cam->cam_mode = sif_072a_mode;
42862306a36Sopenharmony_ci		cam->nmodes = ARRAY_SIZE(sif_072a_mode);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci	sd->expo12a = EXPO12A_DEF;
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* this function is called at probe and resume time */
43562306a36Sopenharmony_cistatic int sd_init_12a(struct gspca_dev *gspca_dev)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 012a\n");
43862306a36Sopenharmony_ci	init_161rev12A(gspca_dev);
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_cistatic int sd_init_72a(struct gspca_dev *gspca_dev)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "Chip revision: 072a\n");
44462306a36Sopenharmony_ci	write_vector(gspca_dev, rev72a_reset);
44562306a36Sopenharmony_ci	msleep(200);
44662306a36Sopenharmony_ci	write_vector(gspca_dev, rev72a_init_data1);
44762306a36Sopenharmony_ci	write_sensor_72a(gspca_dev, rev72a_init_sensor1);
44862306a36Sopenharmony_ci	write_vector(gspca_dev, rev72a_init_data2);
44962306a36Sopenharmony_ci	write_sensor_72a(gspca_dev, rev72a_init_sensor2);
45062306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8112, 0x30);
45162306a36Sopenharmony_ci	return 0;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
45762306a36Sopenharmony_ci	__u16 reg;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (sd->chip_revision == Rev012A)
46062306a36Sopenharmony_ci		reg = 0x8610;
46162306a36Sopenharmony_ci	else
46262306a36Sopenharmony_ci		reg = 0x8611;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	reg_w_val(gspca_dev, reg + 0, val);		/* R */
46562306a36Sopenharmony_ci	reg_w_val(gspca_dev, reg + 1, val);		/* Gr */
46662306a36Sopenharmony_ci	reg_w_val(gspca_dev, reg + 2, val);		/* B */
46762306a36Sopenharmony_ci	reg_w_val(gspca_dev, reg + 3, val);		/* Gb */
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
47362306a36Sopenharmony_ci	__u8 blue, red;
47462306a36Sopenharmony_ci	__u16 reg;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* try to emulate MS-win as possible */
47762306a36Sopenharmony_ci	red = 0x20 + white * 3 / 8;
47862306a36Sopenharmony_ci	blue = 0x90 - white * 5 / 8;
47962306a36Sopenharmony_ci	if (sd->chip_revision == Rev012A) {
48062306a36Sopenharmony_ci		reg = 0x8614;
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		reg = 0x8651;
48362306a36Sopenharmony_ci		red += contrast - 0x20;
48462306a36Sopenharmony_ci		blue += contrast - 0x20;
48562306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8652, contrast + 0x20); /* Gr */
48662306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8654, contrast + 0x20); /* Gb */
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci	reg_w_val(gspca_dev, reg, red);
48962306a36Sopenharmony_ci	reg_w_val(gspca_dev, reg + 2, blue);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci/* rev 12a only */
49362306a36Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 val)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	int i, expo = 0;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* Register 0x8309 controls exposure for the spca561,
49862306a36Sopenharmony_ci	   the basic exposure setting goes from 1-2047, where 1 is completely
49962306a36Sopenharmony_ci	   dark and 2047 is very bright. It not only influences exposure but
50062306a36Sopenharmony_ci	   also the framerate (to allow for longer exposure) from 1 - 300 it
50162306a36Sopenharmony_ci	   only raises the exposure time then from 300 - 600 it halves the
50262306a36Sopenharmony_ci	   framerate to be able to further raise the exposure time and for every
50362306a36Sopenharmony_ci	   300 more it halves the framerate again. This allows for a maximum
50462306a36Sopenharmony_ci	   exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps).
50562306a36Sopenharmony_ci	   Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12
50662306a36Sopenharmony_ci	   configure a divider for the base framerate which us used at the
50762306a36Sopenharmony_ci	   exposure setting of 1-300. These bits configure the base framerate
50862306a36Sopenharmony_ci	   according to the following formula: fps = 60 / (value + 2) */
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* We choose to use the high bits setting the fixed framerate divisor
51162306a36Sopenharmony_ci	   asap, as setting high basic exposure setting without the fixed
51262306a36Sopenharmony_ci	   divider in combination with high gains makes the cam stop */
51362306a36Sopenharmony_ci	static const int table[] =  { 0, 450, 550, 625, EXPOSURE_MAX };
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
51662306a36Sopenharmony_ci		if (val <= table[i + 1]) {
51762306a36Sopenharmony_ci			expo  = val - table[i];
51862306a36Sopenharmony_ci			if (i)
51962306a36Sopenharmony_ci				expo += 300;
52062306a36Sopenharmony_ci			expo |= i << 11;
52162306a36Sopenharmony_ci			break;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = expo;
52662306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = expo >> 8;
52762306a36Sopenharmony_ci	reg_w_buf(gspca_dev, 0x8309, 2);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/* rev 12a only */
53162306a36Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	/* gain reg low 6 bits  0-63 gain, bit 6 and 7, both double the
53462306a36Sopenharmony_ci	   sensitivity when set, so 31 + one of them set == 63, and 15
53562306a36Sopenharmony_ci	   with both of them set == 63 */
53662306a36Sopenharmony_ci	if (val < 64)
53762306a36Sopenharmony_ci		gspca_dev->usb_buf[0] = val;
53862306a36Sopenharmony_ci	else if (val < 128)
53962306a36Sopenharmony_ci		gspca_dev->usb_buf[0] = (val / 2) | 0x40;
54062306a36Sopenharmony_ci	else
54162306a36Sopenharmony_ci		gspca_dev->usb_buf[0] = (val / 4) | 0xc0;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = 0;
54462306a36Sopenharmony_ci	reg_w_buf(gspca_dev, 0x8335, 2);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void setautogain(struct gspca_dev *gspca_dev, s32 val)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (val)
55262306a36Sopenharmony_ci		sd->ag_cnt = AG_CNT_START;
55362306a36Sopenharmony_ci	else
55462306a36Sopenharmony_ci		sd->ag_cnt = -1;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int sd_start_12a(struct gspca_dev *gspca_dev)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	int mode;
56062306a36Sopenharmony_ci	static const __u8 Reg8391[8] =
56162306a36Sopenharmony_ci		{0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
56462306a36Sopenharmony_ci	if (mode <= 1) {
56562306a36Sopenharmony_ci		/* Use compression on 320x240 and above */
56662306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8500, 0x10 | mode);
56762306a36Sopenharmony_ci	} else {
56862306a36Sopenharmony_ci		/* I couldn't get the compression to work below 320x240
56962306a36Sopenharmony_ci		 * Fortunately at these resolutions the bandwidth
57062306a36Sopenharmony_ci		 * is sufficient to push raw frames at ~20fps */
57162306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8500, mode);
57262306a36Sopenharmony_ci	}		/* -- qq@kuku.eu.org */
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = 0xaa;
57562306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x00;
57662306a36Sopenharmony_ci	reg_w_buf(gspca_dev, 0x8307, 2);
57762306a36Sopenharmony_ci	/* clock - lower 0x8X values lead to fps > 30 */
57862306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8700, 0x8a);
57962306a36Sopenharmony_ci					/* 0x8f 0x85 0x27 clock */
58062306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8112, 0x1e | 0x20);
58162306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x850b, 0x03);
58262306a36Sopenharmony_ci	memcpy(gspca_dev->usb_buf, Reg8391, 8);
58362306a36Sopenharmony_ci	reg_w_buf(gspca_dev, 0x8391, 8);
58462306a36Sopenharmony_ci	reg_w_buf(gspca_dev, 0x8390, 8);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* Led ON (bit 3 -> 0 */
58762306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8114, 0x00);
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_cistatic int sd_start_72a(struct gspca_dev *gspca_dev)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
59362306a36Sopenharmony_ci	int Clck;
59462306a36Sopenharmony_ci	int mode;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	write_vector(gspca_dev, rev72a_reset);
59762306a36Sopenharmony_ci	msleep(200);
59862306a36Sopenharmony_ci	write_vector(gspca_dev, rev72a_init_data1);
59962306a36Sopenharmony_ci	write_sensor_72a(gspca_dev, rev72a_init_sensor1);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
60262306a36Sopenharmony_ci	switch (mode) {
60362306a36Sopenharmony_ci	default:
60462306a36Sopenharmony_ci	case 0:
60562306a36Sopenharmony_ci		Clck = 0x27;		/* ms-win 0x87 */
60662306a36Sopenharmony_ci		break;
60762306a36Sopenharmony_ci	case 1:
60862306a36Sopenharmony_ci		Clck = 0x25;
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	case 2:
61162306a36Sopenharmony_ci		Clck = 0x22;
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	case 3:
61462306a36Sopenharmony_ci		Clck = 0x21;
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8700, Clck);	/* 0x27 clock */
61862306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8702, 0x81);
61962306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8500, mode);	/* mode */
62062306a36Sopenharmony_ci	write_sensor_72a(gspca_dev, rev72a_init_sensor2);
62162306a36Sopenharmony_ci	setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue),
62262306a36Sopenharmony_ci			v4l2_ctrl_g_ctrl(sd->contrast));
62362306a36Sopenharmony_ci/*	setbrightness(gspca_dev);	 * fixme: bad values */
62462306a36Sopenharmony_ci	setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
62562306a36Sopenharmony_ci	reg_w_val(gspca_dev, 0x8112, 0x10 | 0x20);
62662306a36Sopenharmony_ci	return 0;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (sd->chip_revision == Rev012A) {
63462306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8112, 0x0e);
63562306a36Sopenharmony_ci		/* Led Off (bit 3 -> 1 */
63662306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8114, 0x08);
63762306a36Sopenharmony_ci	} else {
63862306a36Sopenharmony_ci		reg_w_val(gspca_dev, 0x8112, 0x20);
63962306a36Sopenharmony_ci/*		reg_w_val(gspca_dev, 0x8102, 0x00); ?? */
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void do_autogain(struct gspca_dev *gspca_dev)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
64662306a36Sopenharmony_ci	int expotimes;
64762306a36Sopenharmony_ci	int pixelclk;
64862306a36Sopenharmony_ci	int gainG;
64962306a36Sopenharmony_ci	__u8 R, Gr, Gb, B;
65062306a36Sopenharmony_ci	int y;
65162306a36Sopenharmony_ci	__u8 luma_mean = 110;
65262306a36Sopenharmony_ci	__u8 luma_delta = 20;
65362306a36Sopenharmony_ci	__u8 spring = 4;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (sd->ag_cnt < 0)
65662306a36Sopenharmony_ci		return;
65762306a36Sopenharmony_ci	if (--sd->ag_cnt >= 0)
65862306a36Sopenharmony_ci		return;
65962306a36Sopenharmony_ci	sd->ag_cnt = AG_CNT_START;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	switch (sd->chip_revision) {
66262306a36Sopenharmony_ci	case Rev072A:
66362306a36Sopenharmony_ci		reg_r(gspca_dev, 0x8621, 1);
66462306a36Sopenharmony_ci		Gr = gspca_dev->usb_buf[0];
66562306a36Sopenharmony_ci		reg_r(gspca_dev, 0x8622, 1);
66662306a36Sopenharmony_ci		R = gspca_dev->usb_buf[0];
66762306a36Sopenharmony_ci		reg_r(gspca_dev, 0x8623, 1);
66862306a36Sopenharmony_ci		B = gspca_dev->usb_buf[0];
66962306a36Sopenharmony_ci		reg_r(gspca_dev, 0x8624, 1);
67062306a36Sopenharmony_ci		Gb = gspca_dev->usb_buf[0];
67162306a36Sopenharmony_ci		y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8;
67262306a36Sopenharmony_ci		/* u= (128*B-(43*(Gr+Gb+R))) >> 8; */
67362306a36Sopenharmony_ci		/* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		if (y < luma_mean - luma_delta ||
67662306a36Sopenharmony_ci		    y > luma_mean + luma_delta) {
67762306a36Sopenharmony_ci			expotimes = i2c_read(gspca_dev, 0x09, 0x10);
67862306a36Sopenharmony_ci			pixelclk = 0x0800;
67962306a36Sopenharmony_ci			expotimes = expotimes & 0x07ff;
68062306a36Sopenharmony_ci			gainG = i2c_read(gspca_dev, 0x35, 0x10);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci			expotimes += (luma_mean - y) >> spring;
68362306a36Sopenharmony_ci			gainG += (luma_mean - y) / 50;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci			if (gainG > 0x3f)
68662306a36Sopenharmony_ci				gainG = 0x3f;
68762306a36Sopenharmony_ci			else if (gainG < 3)
68862306a36Sopenharmony_ci				gainG = 3;
68962306a36Sopenharmony_ci			i2c_write(gspca_dev, gainG, 0x35);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci			if (expotimes > 0x0256)
69262306a36Sopenharmony_ci				expotimes = 0x0256;
69362306a36Sopenharmony_ci			else if (expotimes < 3)
69462306a36Sopenharmony_ci				expotimes = 3;
69562306a36Sopenharmony_ci			i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci		break;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
70262306a36Sopenharmony_ci			u8 *data,		/* isoc packet */
70362306a36Sopenharmony_ci			int len)		/* iso packet length */
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	len--;
70862306a36Sopenharmony_ci	switch (*data++) {			/* sequence number */
70962306a36Sopenharmony_ci	case 0:					/* start of frame */
71062306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		/* This should never happen */
71362306a36Sopenharmony_ci		if (len < 2) {
71462306a36Sopenharmony_ci			gspca_err(gspca_dev, "Short SOF packet, ignoring\n\n\n\n\n");
71562306a36Sopenharmony_ci			gspca_dev->last_packet_type = DISCARD_PACKET;
71662306a36Sopenharmony_ci			return;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
72062306a36Sopenharmony_ci		if (data[0] & 0x20) {
72162306a36Sopenharmony_ci			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
72262306a36Sopenharmony_ci			input_sync(gspca_dev->input_dev);
72362306a36Sopenharmony_ci			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
72462306a36Sopenharmony_ci			input_sync(gspca_dev->input_dev);
72562306a36Sopenharmony_ci		}
72662306a36Sopenharmony_ci#endif
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		if (data[1] & 0x10) {
72962306a36Sopenharmony_ci			/* compressed bayer */
73062306a36Sopenharmony_ci			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
73162306a36Sopenharmony_ci		} else {
73262306a36Sopenharmony_ci			/* raw bayer (with a header, which we skip) */
73362306a36Sopenharmony_ci			if (sd->chip_revision == Rev012A) {
73462306a36Sopenharmony_ci				data += 20;
73562306a36Sopenharmony_ci				len -= 20;
73662306a36Sopenharmony_ci			} else {
73762306a36Sopenharmony_ci				data += 16;
73862306a36Sopenharmony_ci				len -= 16;
73962306a36Sopenharmony_ci			}
74062306a36Sopenharmony_ci			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci		return;
74362306a36Sopenharmony_ci	case 0xff:			/* drop (empty mpackets) */
74462306a36Sopenharmony_ci		return;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct gspca_dev *gspca_dev =
75262306a36Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
75362306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	gspca_dev->usb_err = 0;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (!gspca_dev->streaming)
75862306a36Sopenharmony_ci		return 0;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	switch (ctrl->id) {
76162306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
76262306a36Sopenharmony_ci		setbrightness(gspca_dev, ctrl->val);
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
76562306a36Sopenharmony_ci		/* hue/contrast control cluster for 72a */
76662306a36Sopenharmony_ci		setwhite(gspca_dev, sd->hue->val, ctrl->val);
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	case V4L2_CID_HUE:
76962306a36Sopenharmony_ci		/* just plain hue control for 12a */
77062306a36Sopenharmony_ci		setwhite(gspca_dev, ctrl->val, 0);
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci	case V4L2_CID_EXPOSURE:
77362306a36Sopenharmony_ci		setexposure(gspca_dev, ctrl->val);
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci	case V4L2_CID_GAIN:
77662306a36Sopenharmony_ci		setgain(gspca_dev, ctrl->val);
77762306a36Sopenharmony_ci		break;
77862306a36Sopenharmony_ci	case V4L2_CID_AUTOGAIN:
77962306a36Sopenharmony_ci		setautogain(gspca_dev, ctrl->val);
78062306a36Sopenharmony_ci		break;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci	return gspca_dev->usb_err;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
78662306a36Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
78762306a36Sopenharmony_ci};
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int sd_init_controls_12a(struct gspca_dev *gspca_dev)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
79462306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 3);
79562306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
79662306a36Sopenharmony_ci			V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
79762306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
79862306a36Sopenharmony_ci			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
79962306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
80062306a36Sopenharmony_ci			V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700);
80162306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
80262306a36Sopenharmony_ci			V4L2_CID_GAIN, 0, 255, 1, 63);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (hdl->error) {
80562306a36Sopenharmony_ci		pr_err("Could not initialize controls\n");
80662306a36Sopenharmony_ci		return hdl->error;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci	return 0;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic int sd_init_controls_72a(struct gspca_dev *gspca_dev)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
81462306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
81762306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 4);
81862306a36Sopenharmony_ci	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
81962306a36Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20);
82062306a36Sopenharmony_ci	sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
82162306a36Sopenharmony_ci			V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
82262306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
82362306a36Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20);
82462306a36Sopenharmony_ci	sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
82562306a36Sopenharmony_ci			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (hdl->error) {
82862306a36Sopenharmony_ci		pr_err("Could not initialize controls\n");
82962306a36Sopenharmony_ci		return hdl->error;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci	v4l2_ctrl_cluster(2, &sd->contrast);
83262306a36Sopenharmony_ci	return 0;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci/* sub-driver description */
83662306a36Sopenharmony_cistatic const struct sd_desc sd_desc_12a = {
83762306a36Sopenharmony_ci	.name = MODULE_NAME,
83862306a36Sopenharmony_ci	.init_controls = sd_init_controls_12a,
83962306a36Sopenharmony_ci	.config = sd_config,
84062306a36Sopenharmony_ci	.init = sd_init_12a,
84162306a36Sopenharmony_ci	.start = sd_start_12a,
84262306a36Sopenharmony_ci	.stopN = sd_stopN,
84362306a36Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
84462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
84562306a36Sopenharmony_ci	.other_input = 1,
84662306a36Sopenharmony_ci#endif
84762306a36Sopenharmony_ci};
84862306a36Sopenharmony_cistatic const struct sd_desc sd_desc_72a = {
84962306a36Sopenharmony_ci	.name = MODULE_NAME,
85062306a36Sopenharmony_ci	.init_controls = sd_init_controls_72a,
85162306a36Sopenharmony_ci	.config = sd_config,
85262306a36Sopenharmony_ci	.init = sd_init_72a,
85362306a36Sopenharmony_ci	.start = sd_start_72a,
85462306a36Sopenharmony_ci	.stopN = sd_stopN,
85562306a36Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
85662306a36Sopenharmony_ci	.dq_callback = do_autogain,
85762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
85862306a36Sopenharmony_ci	.other_input = 1,
85962306a36Sopenharmony_ci#endif
86062306a36Sopenharmony_ci};
86162306a36Sopenharmony_cistatic const struct sd_desc *sd_desc[2] = {
86262306a36Sopenharmony_ci	&sd_desc_12a,
86362306a36Sopenharmony_ci	&sd_desc_72a
86462306a36Sopenharmony_ci};
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/* -- module initialisation -- */
86762306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = {
86862306a36Sopenharmony_ci	{USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A},
86962306a36Sopenharmony_ci	{USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A},
87062306a36Sopenharmony_ci	{USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A},
87162306a36Sopenharmony_ci	{USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A},
87262306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A},
87362306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A},
87462306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A},
87562306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A},
87662306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A},
87762306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A},
87862306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A},
87962306a36Sopenharmony_ci	{USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A},
88062306a36Sopenharmony_ci	{USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A},
88162306a36Sopenharmony_ci	{USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A},
88262306a36Sopenharmony_ci	{USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A},
88362306a36Sopenharmony_ci	{USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A},
88462306a36Sopenharmony_ci	{}
88562306a36Sopenharmony_ci};
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci/* -- device connect -- */
89062306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
89162306a36Sopenharmony_ci		    const struct usb_device_id *id)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	return gspca_dev_probe(intf, id,
89462306a36Sopenharmony_ci				sd_desc[id->driver_info],
89562306a36Sopenharmony_ci				sizeof(struct sd),
89662306a36Sopenharmony_ci			       THIS_MODULE);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic struct usb_driver sd_driver = {
90062306a36Sopenharmony_ci	.name = MODULE_NAME,
90162306a36Sopenharmony_ci	.id_table = device_table,
90262306a36Sopenharmony_ci	.probe = sd_probe,
90362306a36Sopenharmony_ci	.disconnect = gspca_disconnect,
90462306a36Sopenharmony_ci#ifdef CONFIG_PM
90562306a36Sopenharmony_ci	.suspend = gspca_suspend,
90662306a36Sopenharmony_ci	.resume = gspca_resume,
90762306a36Sopenharmony_ci	.reset_resume = gspca_resume,
90862306a36Sopenharmony_ci#endif
90962306a36Sopenharmony_ci};
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cimodule_usb_driver(sd_driver);
912