162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Mars MR97310A library
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
662306a36Sopenharmony_ci * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
962306a36Sopenharmony_ci * and for the routines for detecting and classifying these various cameras,
1062306a36Sopenharmony_ci * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Support for the control settings for the CIF cameras is
1362306a36Sopenharmony_ci * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and
1462306a36Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li>
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Support for the control settings for the VGA cameras is
1762306a36Sopenharmony_ci * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Several previously unsupported cameras are owned and have been tested by
2062306a36Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> and
2162306a36Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li> and
2262306a36Sopenharmony_ci * Theodore Kilgore <kilgota@auburn.edu> and
2362306a36Sopenharmony_ci * Edmond Rodriguez <erodrig_97@yahoo.com> and
2462306a36Sopenharmony_ci * Aurelien Jacobs <aurel@gnuage.org>
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * The MR97311A support in gspca/mars.c has been helpful in understanding some
2762306a36Sopenharmony_ci * of the registers in these cameras.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define MODULE_NAME "mr97310a"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "gspca.h"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define CAM_TYPE_CIF			0
3762306a36Sopenharmony_ci#define CAM_TYPE_VGA			1
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define MR97310A_BRIGHTNESS_DEFAULT	0
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MR97310A_EXPOSURE_MIN		0
4262306a36Sopenharmony_ci#define MR97310A_EXPOSURE_MAX		4095
4362306a36Sopenharmony_ci#define MR97310A_EXPOSURE_DEFAULT	1000
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define MR97310A_GAIN_MIN		0
4662306a36Sopenharmony_ci#define MR97310A_GAIN_MAX		31
4762306a36Sopenharmony_ci#define MR97310A_GAIN_DEFAULT		25
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define MR97310A_CONTRAST_MIN		0
5062306a36Sopenharmony_ci#define MR97310A_CONTRAST_MAX		31
5162306a36Sopenharmony_ci#define MR97310A_CONTRAST_DEFAULT	23
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define MR97310A_CS_GAIN_MIN		0
5462306a36Sopenharmony_ci#define MR97310A_CS_GAIN_MAX		0x7ff
5562306a36Sopenharmony_ci#define MR97310A_CS_GAIN_DEFAULT	0x110
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
5862306a36Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MIN	3
5962306a36Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MAX	8
6062306a36Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_DEFAULT	3
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciMODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,Theodore Kilgore <kilgota@auburn.edu>");
6362306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
6462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* global parameters */
6762306a36Sopenharmony_cistatic int force_sensor_type = -1;
6862306a36Sopenharmony_cimodule_param(force_sensor_type, int, 0644);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* specific webcam descriptor */
7262306a36Sopenharmony_cistruct sd {
7362306a36Sopenharmony_ci	struct gspca_dev gspca_dev;  /* !! must be the first item */
7462306a36Sopenharmony_ci	struct { /* exposure/min_clockdiv control cluster */
7562306a36Sopenharmony_ci		struct v4l2_ctrl *exposure;
7662306a36Sopenharmony_ci		struct v4l2_ctrl *min_clockdiv;
7762306a36Sopenharmony_ci	};
7862306a36Sopenharmony_ci	u8 sof_read;
7962306a36Sopenharmony_ci	u8 cam_type;	/* 0 is CIF and 1 is VGA */
8062306a36Sopenharmony_ci	u8 sensor_type;	/* We use 0 and 1 here, too. */
8162306a36Sopenharmony_ci	u8 do_lcd_stop;
8262306a36Sopenharmony_ci	u8 adj_colors;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct sensor_w_data {
8662306a36Sopenharmony_ci	u8 reg;
8762306a36Sopenharmony_ci	u8 flags;
8862306a36Sopenharmony_ci	u8 data[16];
8962306a36Sopenharmony_ci	int len;
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = {
9562306a36Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
9662306a36Sopenharmony_ci		.bytesperline = 160,
9762306a36Sopenharmony_ci		.sizeimage = 160 * 120,
9862306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
9962306a36Sopenharmony_ci		.priv = 4},
10062306a36Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
10162306a36Sopenharmony_ci		.bytesperline = 176,
10262306a36Sopenharmony_ci		.sizeimage = 176 * 144,
10362306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
10462306a36Sopenharmony_ci		.priv = 3},
10562306a36Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
10662306a36Sopenharmony_ci		.bytesperline = 320,
10762306a36Sopenharmony_ci		.sizeimage = 320 * 240,
10862306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
10962306a36Sopenharmony_ci		.priv = 2},
11062306a36Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
11162306a36Sopenharmony_ci		.bytesperline = 352,
11262306a36Sopenharmony_ci		.sizeimage = 352 * 288,
11362306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
11462306a36Sopenharmony_ci		.priv = 1},
11562306a36Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
11662306a36Sopenharmony_ci		.bytesperline = 640,
11762306a36Sopenharmony_ci		.sizeimage = 640 * 480,
11862306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
11962306a36Sopenharmony_ci		.priv = 0},
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* the bytes to write are in gspca_dev->usb_buf */
12362306a36Sopenharmony_cistatic int mr_write(struct gspca_dev *gspca_dev, int len)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int rc;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	rc = usb_bulk_msg(gspca_dev->dev,
12862306a36Sopenharmony_ci			  usb_sndbulkpipe(gspca_dev->dev, 4),
12962306a36Sopenharmony_ci			  gspca_dev->usb_buf, len, NULL, 500);
13062306a36Sopenharmony_ci	if (rc < 0)
13162306a36Sopenharmony_ci		pr_err("reg write [%02x] error %d\n",
13262306a36Sopenharmony_ci		       gspca_dev->usb_buf[0], rc);
13362306a36Sopenharmony_ci	return rc;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* the bytes are read into gspca_dev->usb_buf */
13762306a36Sopenharmony_cistatic int mr_read(struct gspca_dev *gspca_dev, int len)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	int rc;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	rc = usb_bulk_msg(gspca_dev->dev,
14262306a36Sopenharmony_ci			  usb_rcvbulkpipe(gspca_dev->dev, 3),
14362306a36Sopenharmony_ci			  gspca_dev->usb_buf, len, NULL, 500);
14462306a36Sopenharmony_ci	if (rc < 0)
14562306a36Sopenharmony_ci		pr_err("reg read [%02x] error %d\n",
14662306a36Sopenharmony_ci		       gspca_dev->usb_buf[0], rc);
14762306a36Sopenharmony_ci	return rc;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags,
15162306a36Sopenharmony_ci	const u8 *data, int len)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x1f;
15462306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = flags;
15562306a36Sopenharmony_ci	gspca_dev->usb_buf[2] = reg;
15662306a36Sopenharmony_ci	memcpy(gspca_dev->usb_buf + 3, data, len);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return mr_write(gspca_dev, len + 3);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int sensor_write_regs(struct gspca_dev *gspca_dev,
16262306a36Sopenharmony_ci	const struct sensor_w_data *data, int len)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	int i, rc;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
16762306a36Sopenharmony_ci		rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags,
16862306a36Sopenharmony_ci					  data[i].data, data[i].len);
16962306a36Sopenharmony_ci		if (rc < 0)
17062306a36Sopenharmony_ci			return rc;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
17962306a36Sopenharmony_ci	u8 buf, confirm_reg;
18062306a36Sopenharmony_ci	int rc;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	buf = data;
18362306a36Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF) {
18462306a36Sopenharmony_ci		rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
18562306a36Sopenharmony_ci		confirm_reg = sd->sensor_type ? 0x13 : 0x11;
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
18862306a36Sopenharmony_ci		confirm_reg = 0x11;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	if (rc < 0)
19162306a36Sopenharmony_ci		return rc;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	buf = 0x01;
19462306a36Sopenharmony_ci	rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
19562306a36Sopenharmony_ci	if (rc < 0)
19662306a36Sopenharmony_ci		return rc;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int err_code;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = reg;
20662306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 1);
20762306a36Sopenharmony_ci	if (err_code < 0)
20862306a36Sopenharmony_ci		return err_code;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	err_code = mr_read(gspca_dev, 16);
21162306a36Sopenharmony_ci	if (err_code < 0)
21262306a36Sopenharmony_ci		return err_code;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (verbose)
21562306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "Register: %02x reads %02x%02x%02x\n",
21662306a36Sopenharmony_ci			  reg,
21762306a36Sopenharmony_ci			  gspca_dev->usb_buf[0],
21862306a36Sopenharmony_ci			  gspca_dev->usb_buf[1],
21962306a36Sopenharmony_ci			  gspca_dev->usb_buf[2]);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int zero_the_pointer(struct gspca_dev *gspca_dev)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	__u8 *data = gspca_dev->usb_buf;
22762306a36Sopenharmony_ci	int err_code;
22862306a36Sopenharmony_ci	u8 status = 0;
22962306a36Sopenharmony_ci	int tries = 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
23262306a36Sopenharmony_ci	if (err_code < 0)
23362306a36Sopenharmony_ci		return err_code;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	data[0] = 0x19;
23662306a36Sopenharmony_ci	data[1] = 0x51;
23762306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
23862306a36Sopenharmony_ci	if (err_code < 0)
23962306a36Sopenharmony_ci		return err_code;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
24262306a36Sopenharmony_ci	if (err_code < 0)
24362306a36Sopenharmony_ci		return err_code;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	data[0] = 0x19;
24662306a36Sopenharmony_ci	data[1] = 0xba;
24762306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
24862306a36Sopenharmony_ci	if (err_code < 0)
24962306a36Sopenharmony_ci		return err_code;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
25262306a36Sopenharmony_ci	if (err_code < 0)
25362306a36Sopenharmony_ci		return err_code;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	data[0] = 0x19;
25662306a36Sopenharmony_ci	data[1] = 0x00;
25762306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
25862306a36Sopenharmony_ci	if (err_code < 0)
25962306a36Sopenharmony_ci		return err_code;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
26262306a36Sopenharmony_ci	if (err_code < 0)
26362306a36Sopenharmony_ci		return err_code;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	data[0] = 0x19;
26662306a36Sopenharmony_ci	data[1] = 0x00;
26762306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
26862306a36Sopenharmony_ci	if (err_code < 0)
26962306a36Sopenharmony_ci		return err_code;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	while (status != 0x0a && tries < 256) {
27262306a36Sopenharmony_ci		err_code = cam_get_response16(gspca_dev, 0x21, 0);
27362306a36Sopenharmony_ci		status = data[0];
27462306a36Sopenharmony_ci		tries++;
27562306a36Sopenharmony_ci		if (err_code < 0)
27662306a36Sopenharmony_ci			return err_code;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	if (status != 0x0a)
27962306a36Sopenharmony_ci		gspca_err(gspca_dev, "status is %02x\n", status);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	tries = 0;
28262306a36Sopenharmony_ci	while (tries < 4) {
28362306a36Sopenharmony_ci		data[0] = 0x19;
28462306a36Sopenharmony_ci		data[1] = 0x00;
28562306a36Sopenharmony_ci		err_code = mr_write(gspca_dev, 2);
28662306a36Sopenharmony_ci		if (err_code < 0)
28762306a36Sopenharmony_ci			return err_code;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		err_code = cam_get_response16(gspca_dev, 0x21, 0);
29062306a36Sopenharmony_ci		tries++;
29162306a36Sopenharmony_ci		if (err_code < 0)
29262306a36Sopenharmony_ci			return err_code;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	data[0] = 0x19;
29662306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 1);
29762306a36Sopenharmony_ci	if (err_code < 0)
29862306a36Sopenharmony_ci		return err_code;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	err_code = mr_read(gspca_dev, 16);
30162306a36Sopenharmony_ci	if (err_code < 0)
30262306a36Sopenharmony_ci		return err_code;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return 0;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int stream_start(struct gspca_dev *gspca_dev)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x01;
31062306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x01;
31162306a36Sopenharmony_ci	return mr_write(gspca_dev, 2);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void stream_stop(struct gspca_dev *gspca_dev)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x01;
31762306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x00;
31862306a36Sopenharmony_ci	if (mr_write(gspca_dev, 2) < 0)
31962306a36Sopenharmony_ci		gspca_err(gspca_dev, "Stream Stop failed\n");
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void lcd_stop(struct gspca_dev *gspca_dev)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x19;
32562306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x54;
32662306a36Sopenharmony_ci	if (mr_write(gspca_dev, 2) < 0)
32762306a36Sopenharmony_ci		gspca_err(gspca_dev, "LCD Stop failed\n");
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int isoc_enable(struct gspca_dev *gspca_dev)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x00;
33362306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x4d;  /* ISOC transferring enable... */
33462306a36Sopenharmony_ci	return mr_write(gspca_dev, 2);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/* This function is called at probe time */
33862306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
33962306a36Sopenharmony_ci		     const struct usb_device_id *id)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
34262306a36Sopenharmony_ci	struct cam *cam;
34362306a36Sopenharmony_ci	int err_code;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	cam = &gspca_dev->cam;
34662306a36Sopenharmony_ci	cam->cam_mode = vga_mode;
34762306a36Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(vga_mode);
34862306a36Sopenharmony_ci	sd->do_lcd_stop = 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Several of the supported CIF cameras share the same USB ID but
35162306a36Sopenharmony_ci	 * require different initializations and different control settings.
35262306a36Sopenharmony_ci	 * The same is true of the VGA cameras. Therefore, we are forced
35362306a36Sopenharmony_ci	 * to start the initialization process in order to determine which
35462306a36Sopenharmony_ci	 * camera is present. Some of the supported cameras require the
35562306a36Sopenharmony_ci	 * memory pointer to be set to 0 as the very first item of business
35662306a36Sopenharmony_ci	 * or else they will not stream. So we do that immediately.
35762306a36Sopenharmony_ci	 */
35862306a36Sopenharmony_ci	err_code = zero_the_pointer(gspca_dev);
35962306a36Sopenharmony_ci	if (err_code < 0)
36062306a36Sopenharmony_ci		return err_code;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	err_code = stream_start(gspca_dev);
36362306a36Sopenharmony_ci	if (err_code < 0)
36462306a36Sopenharmony_ci		return err_code;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Now, the query for sensor type. */
36762306a36Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x07, 1);
36862306a36Sopenharmony_ci	if (err_code < 0)
36962306a36Sopenharmony_ci		return err_code;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
37262306a36Sopenharmony_ci		sd->cam_type = CAM_TYPE_CIF;
37362306a36Sopenharmony_ci		cam->nmodes--;
37462306a36Sopenharmony_ci		/*
37562306a36Sopenharmony_ci		 * All but one of the known CIF cameras share the same USB ID,
37662306a36Sopenharmony_ci		 * but two different init routines are in use, and the control
37762306a36Sopenharmony_ci		 * settings are different, too. We need to detect which camera
37862306a36Sopenharmony_ci		 * of the two known varieties is connected!
37962306a36Sopenharmony_ci		 *
38062306a36Sopenharmony_ci		 * A list of known CIF cameras follows. They all report either
38162306a36Sopenharmony_ci		 * 0200 for type 0 or 0300 for type 1.
38262306a36Sopenharmony_ci		 * If you have another to report, please do
38362306a36Sopenharmony_ci		 *
38462306a36Sopenharmony_ci		 * Name		sd->sensor_type		reported by
38562306a36Sopenharmony_ci		 *
38662306a36Sopenharmony_ci		 * Sakar 56379 Spy-shot	0		T. Kilgore
38762306a36Sopenharmony_ci		 * Innovage		0		T. Kilgore
38862306a36Sopenharmony_ci		 * Vivitar Mini		0		H. De Goede
38962306a36Sopenharmony_ci		 * Vivitar Mini		0		E. Rodriguez
39062306a36Sopenharmony_ci		 * Vivitar Mini		1		T. Kilgore
39162306a36Sopenharmony_ci		 * Elta-Media 8212dc	1		T. Kaiser
39262306a36Sopenharmony_ci		 * Philips dig. keych.	1		T. Kilgore
39362306a36Sopenharmony_ci		 * Trust Spyc@m 100	1		A. Jacobs
39462306a36Sopenharmony_ci		 */
39562306a36Sopenharmony_ci		switch (gspca_dev->usb_buf[0]) {
39662306a36Sopenharmony_ci		case 2:
39762306a36Sopenharmony_ci			sd->sensor_type = 0;
39862306a36Sopenharmony_ci			break;
39962306a36Sopenharmony_ci		case 3:
40062306a36Sopenharmony_ci			sd->sensor_type = 1;
40162306a36Sopenharmony_ci			break;
40262306a36Sopenharmony_ci		default:
40362306a36Sopenharmony_ci			pr_err("Unknown CIF Sensor id : %02x\n",
40462306a36Sopenharmony_ci			       gspca_dev->usb_buf[1]);
40562306a36Sopenharmony_ci			return -ENODEV;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "MR97310A CIF camera detected, sensor: %d\n",
40862306a36Sopenharmony_ci			  sd->sensor_type);
40962306a36Sopenharmony_ci	} else {
41062306a36Sopenharmony_ci		sd->cam_type = CAM_TYPE_VGA;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/*
41362306a36Sopenharmony_ci		 * Here is a table of the responses to the query for sensor
41462306a36Sopenharmony_ci		 * type, from the known MR97310A VGA cameras. Six different
41562306a36Sopenharmony_ci		 * cameras of which five share the same USB ID.
41662306a36Sopenharmony_ci		 *
41762306a36Sopenharmony_ci		 * Name			gspca_dev->usb_buf[]	sd->sensor_type
41862306a36Sopenharmony_ci		 *				sd->do_lcd_stop
41962306a36Sopenharmony_ci		 * Aiptek Pencam VGA+	0300		0		1
42062306a36Sopenharmony_ci		 * ION digital		0300		0		1
42162306a36Sopenharmony_ci		 * Argus DC-1620	0450		1		0
42262306a36Sopenharmony_ci		 * Argus QuickClix	0420		1		1
42362306a36Sopenharmony_ci		 * Sakar 77379 Digital	0350		0		1
42462306a36Sopenharmony_ci		 * Sakar 1638x CyberPix	0120		0		2
42562306a36Sopenharmony_ci		 *
42662306a36Sopenharmony_ci		 * Based upon these results, we assume default settings
42762306a36Sopenharmony_ci		 * and then correct as necessary, as follows.
42862306a36Sopenharmony_ci		 *
42962306a36Sopenharmony_ci		 */
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		sd->sensor_type = 1;
43262306a36Sopenharmony_ci		sd->do_lcd_stop = 0;
43362306a36Sopenharmony_ci		sd->adj_colors = 0;
43462306a36Sopenharmony_ci		if (gspca_dev->usb_buf[0] == 0x01) {
43562306a36Sopenharmony_ci			sd->sensor_type = 2;
43662306a36Sopenharmony_ci		} else if ((gspca_dev->usb_buf[0] != 0x03) &&
43762306a36Sopenharmony_ci					(gspca_dev->usb_buf[0] != 0x04)) {
43862306a36Sopenharmony_ci			pr_err("Unknown VGA Sensor id Byte 0: %02x\n",
43962306a36Sopenharmony_ci			       gspca_dev->usb_buf[0]);
44062306a36Sopenharmony_ci			pr_err("Defaults assumed, may not work\n");
44162306a36Sopenharmony_ci			pr_err("Please report this\n");
44262306a36Sopenharmony_ci		}
44362306a36Sopenharmony_ci		/* Sakar Digital color needs to be adjusted. */
44462306a36Sopenharmony_ci		if ((gspca_dev->usb_buf[0] == 0x03) &&
44562306a36Sopenharmony_ci					(gspca_dev->usb_buf[1] == 0x50))
44662306a36Sopenharmony_ci			sd->adj_colors = 1;
44762306a36Sopenharmony_ci		if (gspca_dev->usb_buf[0] == 0x04) {
44862306a36Sopenharmony_ci			sd->do_lcd_stop = 1;
44962306a36Sopenharmony_ci			switch (gspca_dev->usb_buf[1]) {
45062306a36Sopenharmony_ci			case 0x50:
45162306a36Sopenharmony_ci				sd->sensor_type = 0;
45262306a36Sopenharmony_ci				gspca_dbg(gspca_dev, D_PROBE, "sensor_type corrected to 0\n");
45362306a36Sopenharmony_ci				break;
45462306a36Sopenharmony_ci			case 0x20:
45562306a36Sopenharmony_ci				/* Nothing to do here. */
45662306a36Sopenharmony_ci				break;
45762306a36Sopenharmony_ci			default:
45862306a36Sopenharmony_ci				pr_err("Unknown VGA Sensor id Byte 1: %02x\n",
45962306a36Sopenharmony_ci				       gspca_dev->usb_buf[1]);
46062306a36Sopenharmony_ci				pr_err("Defaults assumed, may not work\n");
46162306a36Sopenharmony_ci				pr_err("Please report this\n");
46262306a36Sopenharmony_ci			}
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "MR97310A VGA camera detected, sensor: %d\n",
46562306a36Sopenharmony_ci			  sd->sensor_type);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci	/* Stop streaming as we've started it only to probe the sensor type. */
46862306a36Sopenharmony_ci	sd_stopN(gspca_dev);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (force_sensor_type != -1) {
47162306a36Sopenharmony_ci		sd->sensor_type = !!force_sensor_type;
47262306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "Forcing sensor type to: %d\n",
47362306a36Sopenharmony_ci			  sd->sensor_type);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/* this function is called at probe and resume time */
48062306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int start_cif_cam(struct gspca_dev *gspca_dev)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
48862306a36Sopenharmony_ci	__u8 *data = gspca_dev->usb_buf;
48962306a36Sopenharmony_ci	int err_code;
49062306a36Sopenharmony_ci	static const __u8 startup_string[] = {
49162306a36Sopenharmony_ci		0x00,
49262306a36Sopenharmony_ci		0x0d,
49362306a36Sopenharmony_ci		0x01,
49462306a36Sopenharmony_ci		0x00, /* Hsize/8 for 352 or 320 */
49562306a36Sopenharmony_ci		0x00, /* Vsize/4 for 288 or 240 */
49662306a36Sopenharmony_ci		0x13, /* or 0xbb, depends on sensor */
49762306a36Sopenharmony_ci		0x00, /* Hstart, depends on res. */
49862306a36Sopenharmony_ci		0x00, /* reserved ? */
49962306a36Sopenharmony_ci		0x00, /* Vstart, depends on res. and sensor */
50062306a36Sopenharmony_ci		0x50, /* 0x54 to get 176 or 160 */
50162306a36Sopenharmony_ci		0xc0
50262306a36Sopenharmony_ci	};
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Note: Some of the above descriptions guessed from MR97113A driver */
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	memcpy(data, startup_string, 11);
50762306a36Sopenharmony_ci	if (sd->sensor_type)
50862306a36Sopenharmony_ci		data[5] = 0xbb;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	switch (gspca_dev->pixfmt.width) {
51162306a36Sopenharmony_ci	case 160:
51262306a36Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down from 320 */
51362306a36Sopenharmony_ci		fallthrough;
51462306a36Sopenharmony_ci	case 320:
51562306a36Sopenharmony_ci	default:
51662306a36Sopenharmony_ci		data[3] = 0x28;			   /* reg 2, H size/8 */
51762306a36Sopenharmony_ci		data[4] = 0x3c;			   /* reg 3, V size/4 */
51862306a36Sopenharmony_ci		data[6] = 0x14;			   /* reg 5, H start  */
51962306a36Sopenharmony_ci		data[8] = 0x1a + sd->sensor_type;  /* reg 7, V start  */
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case 176:
52262306a36Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down from 352 */
52362306a36Sopenharmony_ci		fallthrough;
52462306a36Sopenharmony_ci	case 352:
52562306a36Sopenharmony_ci		data[3] = 0x2c;			   /* reg 2, H size/8 */
52662306a36Sopenharmony_ci		data[4] = 0x48;			   /* reg 3, V size/4 */
52762306a36Sopenharmony_ci		data[6] = 0x06;			   /* reg 5, H start  */
52862306a36Sopenharmony_ci		data[8] = 0x06 - sd->sensor_type;  /* reg 7, V start  */
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 11);
53262306a36Sopenharmony_ci	if (err_code < 0)
53362306a36Sopenharmony_ci		return err_code;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (!sd->sensor_type) {
53662306a36Sopenharmony_ci		static const struct sensor_w_data cif_sensor0_init_data[] = {
53762306a36Sopenharmony_ci			{0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01,
53862306a36Sopenharmony_ci				      0x0f, 0x14, 0x0f, 0x10}, 8},
53962306a36Sopenharmony_ci			{0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5},
54062306a36Sopenharmony_ci			{0x12, 0x00, {0x07}, 1},
54162306a36Sopenharmony_ci			{0x1f, 0x00, {0x06}, 1},
54262306a36Sopenharmony_ci			{0x27, 0x00, {0x04}, 1},
54362306a36Sopenharmony_ci			{0x29, 0x00, {0x0c}, 1},
54462306a36Sopenharmony_ci			{0x40, 0x00, {0x40, 0x00, 0x04}, 3},
54562306a36Sopenharmony_ci			{0x50, 0x00, {0x60}, 1},
54662306a36Sopenharmony_ci			{0x60, 0x00, {0x06}, 1},
54762306a36Sopenharmony_ci			{0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6},
54862306a36Sopenharmony_ci			{0x72, 0x00, {0x1e, 0x56}, 2},
54962306a36Sopenharmony_ci			{0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02,
55062306a36Sopenharmony_ci				      0x31, 0x80, 0x00}, 9},
55162306a36Sopenharmony_ci			{0x11, 0x00, {0x01}, 1},
55262306a36Sopenharmony_ci			{0, 0, {0}, 0}
55362306a36Sopenharmony_ci		};
55462306a36Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data,
55562306a36Sopenharmony_ci					 ARRAY_SIZE(cif_sensor0_init_data));
55662306a36Sopenharmony_ci	} else {	/* sd->sensor_type = 1 */
55762306a36Sopenharmony_ci		static const struct sensor_w_data cif_sensor1_init_data[] = {
55862306a36Sopenharmony_ci			/* Reg 3,4, 7,8 get set by the controls */
55962306a36Sopenharmony_ci			{0x02, 0x00, {0x10}, 1},
56062306a36Sopenharmony_ci			{0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */
56162306a36Sopenharmony_ci			{0x06, 0x01, {0x00}, 1},
56262306a36Sopenharmony_ci			{0x09, 0x02, {0x0e}, 1},
56362306a36Sopenharmony_ci			{0x0a, 0x02, {0x05}, 1},
56462306a36Sopenharmony_ci			{0x0b, 0x02, {0x05}, 1},
56562306a36Sopenharmony_ci			{0x0c, 0x02, {0x0f}, 1},
56662306a36Sopenharmony_ci			{0x0d, 0x02, {0x07}, 1},
56762306a36Sopenharmony_ci			{0x0e, 0x02, {0x0c}, 1},
56862306a36Sopenharmony_ci			{0x0f, 0x00, {0x00}, 1},
56962306a36Sopenharmony_ci			{0x10, 0x00, {0x06}, 1},
57062306a36Sopenharmony_ci			{0x11, 0x00, {0x07}, 1},
57162306a36Sopenharmony_ci			{0x12, 0x00, {0x00}, 1},
57262306a36Sopenharmony_ci			{0x13, 0x00, {0x01}, 1},
57362306a36Sopenharmony_ci			{0, 0, {0}, 0}
57462306a36Sopenharmony_ci		};
57562306a36Sopenharmony_ci		/* Without this command the cam won't work with USB-UHCI */
57662306a36Sopenharmony_ci		gspca_dev->usb_buf[0] = 0x0a;
57762306a36Sopenharmony_ci		gspca_dev->usb_buf[1] = 0x00;
57862306a36Sopenharmony_ci		err_code = mr_write(gspca_dev, 2);
57962306a36Sopenharmony_ci		if (err_code < 0)
58062306a36Sopenharmony_ci			return err_code;
58162306a36Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
58262306a36Sopenharmony_ci					 ARRAY_SIZE(cif_sensor1_init_data));
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	return err_code;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int start_vga_cam(struct gspca_dev *gspca_dev)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
59062306a36Sopenharmony_ci	__u8 *data = gspca_dev->usb_buf;
59162306a36Sopenharmony_ci	int err_code;
59262306a36Sopenharmony_ci	static const __u8 startup_string[] =
59362306a36Sopenharmony_ci		{0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00,
59462306a36Sopenharmony_ci		 0x00, 0x50, 0xc0};
59562306a36Sopenharmony_ci	/* What some of these mean is explained in start_cif_cam(), above */
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	memcpy(data, startup_string, 11);
59862306a36Sopenharmony_ci	if (!sd->sensor_type) {
59962306a36Sopenharmony_ci		data[5]  = 0x00;
60062306a36Sopenharmony_ci		data[10] = 0x91;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci	if (sd->sensor_type == 2) {
60362306a36Sopenharmony_ci		data[5]  = 0x00;
60462306a36Sopenharmony_ci		data[10] = 0x18;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	switch (gspca_dev->pixfmt.width) {
60862306a36Sopenharmony_ci	case 160:
60962306a36Sopenharmony_ci		data[9] |= 0x0c;  /* reg 8, 4:1 scale down */
61062306a36Sopenharmony_ci		fallthrough;
61162306a36Sopenharmony_ci	case 320:
61262306a36Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down */
61362306a36Sopenharmony_ci		fallthrough;
61462306a36Sopenharmony_ci	case 640:
61562306a36Sopenharmony_ci	default:
61662306a36Sopenharmony_ci		data[3] = 0x50;  /* reg 2, H size/8 */
61762306a36Sopenharmony_ci		data[4] = 0x78;  /* reg 3, V size/4 */
61862306a36Sopenharmony_ci		data[6] = 0x04;  /* reg 5, H start */
61962306a36Sopenharmony_ci		data[8] = 0x03;  /* reg 7, V start */
62062306a36Sopenharmony_ci		if (sd->sensor_type == 2) {
62162306a36Sopenharmony_ci			data[6] = 2;
62262306a36Sopenharmony_ci			data[8] = 1;
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci		if (sd->do_lcd_stop)
62562306a36Sopenharmony_ci			data[8] = 0x04;  /* Bayer tile shifted */
62662306a36Sopenharmony_ci		break;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	case 176:
62962306a36Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down */
63062306a36Sopenharmony_ci		fallthrough;
63162306a36Sopenharmony_ci	case 352:
63262306a36Sopenharmony_ci		data[3] = 0x2c;  /* reg 2, H size */
63362306a36Sopenharmony_ci		data[4] = 0x48;  /* reg 3, V size */
63462306a36Sopenharmony_ci		data[6] = 0x94;  /* reg 5, H start */
63562306a36Sopenharmony_ci		data[8] = 0x63;  /* reg 7, V start */
63662306a36Sopenharmony_ci		if (sd->do_lcd_stop)
63762306a36Sopenharmony_ci			data[8] = 0x64;  /* Bayer tile shifted */
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	err_code = mr_write(gspca_dev, 11);
64262306a36Sopenharmony_ci	if (err_code < 0)
64362306a36Sopenharmony_ci		return err_code;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (!sd->sensor_type) {
64662306a36Sopenharmony_ci		static const struct sensor_w_data vga_sensor0_init_data[] = {
64762306a36Sopenharmony_ci			{0x01, 0x00, {0x0c, 0x00, 0x04}, 3},
64862306a36Sopenharmony_ci			{0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4},
64962306a36Sopenharmony_ci			{0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4},
65062306a36Sopenharmony_ci			{0x25, 0x00, {0x03, 0xa9, 0x80}, 3},
65162306a36Sopenharmony_ci			{0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4},
65262306a36Sopenharmony_ci			{0, 0, {0}, 0}
65362306a36Sopenharmony_ci		};
65462306a36Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
65562306a36Sopenharmony_ci					 ARRAY_SIZE(vga_sensor0_init_data));
65662306a36Sopenharmony_ci	} else if (sd->sensor_type == 1) {
65762306a36Sopenharmony_ci		static const struct sensor_w_data color_adj[] = {
65862306a36Sopenharmony_ci			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
65962306a36Sopenharmony_ci				/* adjusted blue, green, red gain correct
66062306a36Sopenharmony_ci				   too much blue from the Sakar Digital */
66162306a36Sopenharmony_ci				0x05, 0x01, 0x04}, 8}
66262306a36Sopenharmony_ci		};
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		static const struct sensor_w_data color_no_adj[] = {
66562306a36Sopenharmony_ci			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
66662306a36Sopenharmony_ci				/* default blue, green, red gain settings */
66762306a36Sopenharmony_ci				0x07, 0x00, 0x01}, 8}
66862306a36Sopenharmony_ci		};
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		static const struct sensor_w_data vga_sensor1_init_data[] = {
67162306a36Sopenharmony_ci			{0x11, 0x04, {0x01}, 1},
67262306a36Sopenharmony_ci			{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
67362306a36Sopenharmony_ci			/* These settings may be better for some cameras */
67462306a36Sopenharmony_ci			/* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
67562306a36Sopenharmony_ci				0x00, 0x0a}, 7},
67662306a36Sopenharmony_ci			{0x11, 0x04, {0x01}, 1},
67762306a36Sopenharmony_ci			{0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
67862306a36Sopenharmony_ci			{0x11, 0x04, {0x01}, 1},
67962306a36Sopenharmony_ci			{0, 0, {0}, 0}
68062306a36Sopenharmony_ci		};
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		if (sd->adj_colors)
68362306a36Sopenharmony_ci			err_code = sensor_write_regs(gspca_dev, color_adj,
68462306a36Sopenharmony_ci					 ARRAY_SIZE(color_adj));
68562306a36Sopenharmony_ci		else
68662306a36Sopenharmony_ci			err_code = sensor_write_regs(gspca_dev, color_no_adj,
68762306a36Sopenharmony_ci					 ARRAY_SIZE(color_no_adj));
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (err_code < 0)
69062306a36Sopenharmony_ci			return err_code;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
69362306a36Sopenharmony_ci					 ARRAY_SIZE(vga_sensor1_init_data));
69462306a36Sopenharmony_ci	} else {	/* sensor type == 2 */
69562306a36Sopenharmony_ci		static const struct sensor_w_data vga_sensor2_init_data[] = {
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci			{0x01, 0x00, {0x48}, 1},
69862306a36Sopenharmony_ci			{0x02, 0x00, {0x22}, 1},
69962306a36Sopenharmony_ci			/* Reg 3 msb and 4 is lsb of the exposure setting*/
70062306a36Sopenharmony_ci			{0x05, 0x00, {0x10}, 1},
70162306a36Sopenharmony_ci			{0x06, 0x00, {0x00}, 1},
70262306a36Sopenharmony_ci			{0x07, 0x00, {0x00}, 1},
70362306a36Sopenharmony_ci			{0x08, 0x00, {0x00}, 1},
70462306a36Sopenharmony_ci			{0x09, 0x00, {0x00}, 1},
70562306a36Sopenharmony_ci			/* The following are used in the gain control
70662306a36Sopenharmony_ci			 * which is BTW completely borked in the OEM driver
70762306a36Sopenharmony_ci			 * The values for each color go from 0 to 0x7ff
70862306a36Sopenharmony_ci			 *{0x0a, 0x00, {0x01}, 1},  green1 gain msb
70962306a36Sopenharmony_ci			 *{0x0b, 0x00, {0x10}, 1},  green1 gain lsb
71062306a36Sopenharmony_ci			 *{0x0c, 0x00, {0x01}, 1},  red gain msb
71162306a36Sopenharmony_ci			 *{0x0d, 0x00, {0x10}, 1},  red gain lsb
71262306a36Sopenharmony_ci			 *{0x0e, 0x00, {0x01}, 1},  blue gain msb
71362306a36Sopenharmony_ci			 *{0x0f, 0x00, {0x10}, 1},  blue gain lsb
71462306a36Sopenharmony_ci			 *{0x10, 0x00, {0x01}, 1}, green2 gain msb
71562306a36Sopenharmony_ci			 *{0x11, 0x00, {0x10}, 1}, green2 gain lsb
71662306a36Sopenharmony_ci			 */
71762306a36Sopenharmony_ci			{0x12, 0x00, {0x00}, 1},
71862306a36Sopenharmony_ci			{0x13, 0x00, {0x04}, 1}, /* weird effect on colors */
71962306a36Sopenharmony_ci			{0x14, 0x00, {0x00}, 1},
72062306a36Sopenharmony_ci			{0x15, 0x00, {0x06}, 1},
72162306a36Sopenharmony_ci			{0x16, 0x00, {0x01}, 1},
72262306a36Sopenharmony_ci			{0x17, 0x00, {0xe2}, 1}, /* vertical alignment */
72362306a36Sopenharmony_ci			{0x18, 0x00, {0x02}, 1},
72462306a36Sopenharmony_ci			{0x19, 0x00, {0x82}, 1}, /* don't mess with */
72562306a36Sopenharmony_ci			{0x1a, 0x00, {0x00}, 1},
72662306a36Sopenharmony_ci			{0x1b, 0x00, {0x20}, 1},
72762306a36Sopenharmony_ci			/* {0x1c, 0x00, {0x17}, 1}, contrast control */
72862306a36Sopenharmony_ci			{0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */
72962306a36Sopenharmony_ci			{0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */
73062306a36Sopenharmony_ci			{0x1f, 0x00, {0x0c}, 1},
73162306a36Sopenharmony_ci			{0x20, 0x00, {0x00}, 1},
73262306a36Sopenharmony_ci			{0, 0, {0}, 0}
73362306a36Sopenharmony_ci		};
73462306a36Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data,
73562306a36Sopenharmony_ci					 ARRAY_SIZE(vga_sensor2_init_data));
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci	return err_code;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
74362306a36Sopenharmony_ci	int err_code;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	sd->sof_read = 0;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* Some of the VGA cameras require the memory pointer
74862306a36Sopenharmony_ci	 * to be set to 0 again. We have been forced to start the
74962306a36Sopenharmony_ci	 * stream in sd_config() to detect the hardware, and closed it.
75062306a36Sopenharmony_ci	 * Thus, we need here to do a completely fresh and clean start. */
75162306a36Sopenharmony_ci	err_code = zero_the_pointer(gspca_dev);
75262306a36Sopenharmony_ci	if (err_code < 0)
75362306a36Sopenharmony_ci		return err_code;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	err_code = stream_start(gspca_dev);
75662306a36Sopenharmony_ci	if (err_code < 0)
75762306a36Sopenharmony_ci		return err_code;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF) {
76062306a36Sopenharmony_ci		err_code = start_cif_cam(gspca_dev);
76162306a36Sopenharmony_ci	} else {
76262306a36Sopenharmony_ci		err_code = start_vga_cam(gspca_dev);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	if (err_code < 0)
76562306a36Sopenharmony_ci		return err_code;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return isoc_enable(gspca_dev);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	stream_stop(gspca_dev);
77562306a36Sopenharmony_ci	/* Not all the cams need this, but even if not, probably a good idea */
77662306a36Sopenharmony_ci	zero_the_pointer(gspca_dev);
77762306a36Sopenharmony_ci	if (sd->do_lcd_stop)
77862306a36Sopenharmony_ci		lcd_stop(gspca_dev);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
78462306a36Sopenharmony_ci	u8 sign_reg = 7;  /* This reg and the next one used on CIF cams. */
78562306a36Sopenharmony_ci	u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
78662306a36Sopenharmony_ci	static const u8 quick_clix_table[] =
78762306a36Sopenharmony_ci	/*	  0  1  2   3  4  5  6  7  8  9  10  11  12  13  14  15 */
78862306a36Sopenharmony_ci		{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9,  7, 10, 13, 11, 14, 15};
78962306a36Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_VGA) {
79062306a36Sopenharmony_ci		sign_reg += 4;
79162306a36Sopenharmony_ci		value_reg += 4;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
79562306a36Sopenharmony_ci	if (val > 0) {
79662306a36Sopenharmony_ci		sensor_write1(gspca_dev, sign_reg, 0x00);
79762306a36Sopenharmony_ci	} else {
79862306a36Sopenharmony_ci		sensor_write1(gspca_dev, sign_reg, 0x01);
79962306a36Sopenharmony_ci		val = 257 - val;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci	/* Use lookup table for funky Argus QuickClix brightness */
80262306a36Sopenharmony_ci	if (sd->do_lcd_stop)
80362306a36Sopenharmony_ci		val = quick_clix_table[val];
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	sensor_write1(gspca_dev, value_reg, val);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
81162306a36Sopenharmony_ci	int exposure = MR97310A_EXPOSURE_DEFAULT;
81262306a36Sopenharmony_ci	u8 buf[2];
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
81562306a36Sopenharmony_ci		/* This cam does not like exposure settings < 300,
81662306a36Sopenharmony_ci		   so scale 0 - 4095 to 300 - 4095 */
81762306a36Sopenharmony_ci		exposure = (expo * 9267) / 10000 + 300;
81862306a36Sopenharmony_ci		sensor_write1(gspca_dev, 3, exposure >> 4);
81962306a36Sopenharmony_ci		sensor_write1(gspca_dev, 4, exposure & 0x0f);
82062306a36Sopenharmony_ci	} else if (sd->sensor_type == 2) {
82162306a36Sopenharmony_ci		exposure = expo;
82262306a36Sopenharmony_ci		exposure >>= 3;
82362306a36Sopenharmony_ci		sensor_write1(gspca_dev, 3, exposure >> 8);
82462306a36Sopenharmony_ci		sensor_write1(gspca_dev, 4, exposure & 0xff);
82562306a36Sopenharmony_ci	} else {
82662306a36Sopenharmony_ci		/* We have both a clock divider and an exposure register.
82762306a36Sopenharmony_ci		   We first calculate the clock divider, as that determines
82862306a36Sopenharmony_ci		   the maximum exposure and then we calculate the exposure
82962306a36Sopenharmony_ci		   register setting (which goes from 0 - 511).
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		   Note our 0 - 4095 exposure is mapped to 0 - 511
83262306a36Sopenharmony_ci		   milliseconds exposure time */
83362306a36Sopenharmony_ci		u8 clockdiv = (60 * expo + 7999) / 8000;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		/* Limit framerate to not exceed usb bandwidth */
83662306a36Sopenharmony_ci		if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320)
83762306a36Sopenharmony_ci			clockdiv = min_clockdiv;
83862306a36Sopenharmony_ci		else if (clockdiv < 2)
83962306a36Sopenharmony_ci			clockdiv = 2;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
84262306a36Sopenharmony_ci			clockdiv = 4;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
84562306a36Sopenharmony_ci		exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
84662306a36Sopenharmony_ci		exposure = (60 * 511 * expo) / (8000 * clockdiv);
84762306a36Sopenharmony_ci		if (exposure > 511)
84862306a36Sopenharmony_ci			exposure = 511;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		/* exposure register value is reversed! */
85162306a36Sopenharmony_ci		exposure = 511 - exposure;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		buf[0] = exposure & 0xff;
85462306a36Sopenharmony_ci		buf[1] = exposure >> 8;
85562306a36Sopenharmony_ci		sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
85662306a36Sopenharmony_ci		sensor_write1(gspca_dev, 0x02, clockdiv);
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
86362306a36Sopenharmony_ci	u8 gainreg;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
86662306a36Sopenharmony_ci		sensor_write1(gspca_dev, 0x0e, val);
86762306a36Sopenharmony_ci	else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
86862306a36Sopenharmony_ci		for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
86962306a36Sopenharmony_ci			sensor_write1(gspca_dev, gainreg, val >> 8);
87062306a36Sopenharmony_ci			sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci	else
87362306a36Sopenharmony_ci		sensor_write1(gspca_dev, 0x10, val);
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	sensor_write1(gspca_dev, 0x1c, val);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct gspca_dev *gspca_dev =
88462306a36Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
88562306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	gspca_dev->usb_err = 0;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if (!gspca_dev->streaming)
89062306a36Sopenharmony_ci		return 0;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	switch (ctrl->id) {
89362306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
89462306a36Sopenharmony_ci		setbrightness(gspca_dev, ctrl->val);
89562306a36Sopenharmony_ci		break;
89662306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
89762306a36Sopenharmony_ci		setcontrast(gspca_dev, ctrl->val);
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci	case V4L2_CID_EXPOSURE:
90062306a36Sopenharmony_ci		setexposure(gspca_dev, sd->exposure->val,
90162306a36Sopenharmony_ci			    sd->min_clockdiv ? sd->min_clockdiv->val : 0);
90262306a36Sopenharmony_ci		break;
90362306a36Sopenharmony_ci	case V4L2_CID_GAIN:
90462306a36Sopenharmony_ci		setgain(gspca_dev, ctrl->val);
90562306a36Sopenharmony_ci		break;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	return gspca_dev->usb_err;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
91162306a36Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
91262306a36Sopenharmony_ci};
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
91762306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
91862306a36Sopenharmony_ci	static const struct v4l2_ctrl_config clockdiv = {
91962306a36Sopenharmony_ci		.ops = &sd_ctrl_ops,
92062306a36Sopenharmony_ci		.id = MR97310A_CID_CLOCKDIV,
92162306a36Sopenharmony_ci		.type = V4L2_CTRL_TYPE_INTEGER,
92262306a36Sopenharmony_ci		.name = "Minimum Clock Divider",
92362306a36Sopenharmony_ci		.min = MR97310A_MIN_CLOCKDIV_MIN,
92462306a36Sopenharmony_ci		.max = MR97310A_MIN_CLOCKDIV_MAX,
92562306a36Sopenharmony_ci		.step = 1,
92662306a36Sopenharmony_ci		.def = MR97310A_MIN_CLOCKDIV_DEFAULT,
92762306a36Sopenharmony_ci	};
92862306a36Sopenharmony_ci	bool has_brightness = false;
92962306a36Sopenharmony_ci	bool has_argus_brightness = false;
93062306a36Sopenharmony_ci	bool has_contrast = false;
93162306a36Sopenharmony_ci	bool has_gain = false;
93262306a36Sopenharmony_ci	bool has_cs_gain = false;
93362306a36Sopenharmony_ci	bool has_exposure = false;
93462306a36Sopenharmony_ci	bool has_clockdiv = false;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
93762306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 4);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* Setup controls depending on camera type */
94062306a36Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF) {
94162306a36Sopenharmony_ci		/* No brightness for sensor_type 0 */
94262306a36Sopenharmony_ci		if (sd->sensor_type == 0)
94362306a36Sopenharmony_ci			has_exposure = has_gain = has_clockdiv = true;
94462306a36Sopenharmony_ci		else
94562306a36Sopenharmony_ci			has_exposure = has_gain = has_brightness = true;
94662306a36Sopenharmony_ci	} else {
94762306a36Sopenharmony_ci		/* All controls need to be disabled if VGA sensor_type is 0 */
94862306a36Sopenharmony_ci		if (sd->sensor_type == 0)
94962306a36Sopenharmony_ci			; /* no controls! */
95062306a36Sopenharmony_ci		else if (sd->sensor_type == 2)
95162306a36Sopenharmony_ci			has_exposure = has_cs_gain = has_contrast = true;
95262306a36Sopenharmony_ci		else if (sd->do_lcd_stop)
95362306a36Sopenharmony_ci			has_exposure = has_gain = has_argus_brightness =
95462306a36Sopenharmony_ci				has_clockdiv = true;
95562306a36Sopenharmony_ci		else
95662306a36Sopenharmony_ci			has_exposure = has_gain = has_brightness =
95762306a36Sopenharmony_ci				has_clockdiv = true;
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	/* Separate brightness control description for Argus QuickClix as it has
96162306a36Sopenharmony_ci	 * different limits from the other mr97310a cameras, and separate gain
96262306a36Sopenharmony_ci	 * control for Sakar CyberPix camera. */
96362306a36Sopenharmony_ci	/*
96462306a36Sopenharmony_ci	 * This control is disabled for CIF type 1 and VGA type 0 cameras.
96562306a36Sopenharmony_ci	 * It does not quite act linearly for the Argus QuickClix camera,
96662306a36Sopenharmony_ci	 * but it does control brightness. The values are 0 - 15 only, and
96762306a36Sopenharmony_ci	 * the table above makes them act consecutively.
96862306a36Sopenharmony_ci	 */
96962306a36Sopenharmony_ci	if (has_brightness)
97062306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
97162306a36Sopenharmony_ci			V4L2_CID_BRIGHTNESS, -254, 255, 1,
97262306a36Sopenharmony_ci			MR97310A_BRIGHTNESS_DEFAULT);
97362306a36Sopenharmony_ci	else if (has_argus_brightness)
97462306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
97562306a36Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 15, 1,
97662306a36Sopenharmony_ci			MR97310A_BRIGHTNESS_DEFAULT);
97762306a36Sopenharmony_ci	if (has_contrast)
97862306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
97962306a36Sopenharmony_ci			V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
98062306a36Sopenharmony_ci			MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
98162306a36Sopenharmony_ci	if (has_gain)
98262306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
98362306a36Sopenharmony_ci			V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
98462306a36Sopenharmony_ci			1, MR97310A_GAIN_DEFAULT);
98562306a36Sopenharmony_ci	else if (has_cs_gain)
98662306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
98762306a36Sopenharmony_ci			MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
98862306a36Sopenharmony_ci			1, MR97310A_CS_GAIN_DEFAULT);
98962306a36Sopenharmony_ci	if (has_exposure)
99062306a36Sopenharmony_ci		sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
99162306a36Sopenharmony_ci			V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
99262306a36Sopenharmony_ci			MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
99362306a36Sopenharmony_ci	if (has_clockdiv)
99462306a36Sopenharmony_ci		sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (hdl->error) {
99762306a36Sopenharmony_ci		pr_err("Could not initialize controls\n");
99862306a36Sopenharmony_ci		return hdl->error;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci	if (has_exposure && has_clockdiv)
100162306a36Sopenharmony_ci		v4l2_ctrl_cluster(2, &sd->exposure);
100262306a36Sopenharmony_ci	return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci/* Include pac common sof detection functions */
100662306a36Sopenharmony_ci#include "pac_common.h"
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
100962306a36Sopenharmony_ci			u8 *data,		/* isoc packet */
101062306a36Sopenharmony_ci			int len)		/* iso packet length */
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
101362306a36Sopenharmony_ci	unsigned char *sof;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
101662306a36Sopenharmony_ci	if (sof) {
101762306a36Sopenharmony_ci		int n;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		/* finish decoding current frame */
102062306a36Sopenharmony_ci		n = sof - data;
102162306a36Sopenharmony_ci		if (n > sizeof pac_sof_marker)
102262306a36Sopenharmony_ci			n -= sizeof pac_sof_marker;
102362306a36Sopenharmony_ci		else
102462306a36Sopenharmony_ci			n = 0;
102562306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET,
102662306a36Sopenharmony_ci					data, n);
102762306a36Sopenharmony_ci		/* Start next frame. */
102862306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET,
102962306a36Sopenharmony_ci			pac_sof_marker, sizeof pac_sof_marker);
103062306a36Sopenharmony_ci		len -= sof - data;
103162306a36Sopenharmony_ci		data = sof;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci/* sub-driver description */
103762306a36Sopenharmony_cistatic const struct sd_desc sd_desc = {
103862306a36Sopenharmony_ci	.name = MODULE_NAME,
103962306a36Sopenharmony_ci	.config = sd_config,
104062306a36Sopenharmony_ci	.init = sd_init,
104162306a36Sopenharmony_ci	.init_controls = sd_init_controls,
104262306a36Sopenharmony_ci	.start = sd_start,
104362306a36Sopenharmony_ci	.stopN = sd_stopN,
104462306a36Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
104562306a36Sopenharmony_ci};
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci/* -- module initialisation -- */
104862306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = {
104962306a36Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x0110)},	/* Trust Spyc@m 100 */
105062306a36Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x0111)},	/* Aiptek Pencam VGA+ */
105162306a36Sopenharmony_ci	{USB_DEVICE(0x093a, 0x010f)},	/* All other known MR97310A VGA cams */
105262306a36Sopenharmony_ci	{USB_DEVICE(0x093a, 0x010e)},	/* All known MR97310A CIF cams */
105362306a36Sopenharmony_ci	{}
105462306a36Sopenharmony_ci};
105562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci/* -- device connect -- */
105862306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
105962306a36Sopenharmony_ci		    const struct usb_device_id *id)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
106262306a36Sopenharmony_ci			       THIS_MODULE);
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic struct usb_driver sd_driver = {
106662306a36Sopenharmony_ci	.name = MODULE_NAME,
106762306a36Sopenharmony_ci	.id_table = device_table,
106862306a36Sopenharmony_ci	.probe = sd_probe,
106962306a36Sopenharmony_ci	.disconnect = gspca_disconnect,
107062306a36Sopenharmony_ci#ifdef CONFIG_PM
107162306a36Sopenharmony_ci	.suspend = gspca_suspend,
107262306a36Sopenharmony_ci	.resume = gspca_resume,
107362306a36Sopenharmony_ci	.reset_resume = gspca_resume,
107462306a36Sopenharmony_ci#endif
107562306a36Sopenharmony_ci};
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cimodule_usb_driver(sd_driver);
1078