18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Mars MR97310A library
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
98c2ecf20Sopenharmony_ci * and for the routines for detecting and classifying these various cameras,
108c2ecf20Sopenharmony_ci * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Support for the control settings for the CIF cameras is
138c2ecf20Sopenharmony_ci * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and
148c2ecf20Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li>
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Support for the control settings for the VGA cameras is
178c2ecf20Sopenharmony_ci * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Several previously unsupported cameras are owned and have been tested by
208c2ecf20Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> and
218c2ecf20Sopenharmony_ci * Thomas Kaiser <thomas@kaiser-linux.li> and
228c2ecf20Sopenharmony_ci * Theodore Kilgore <kilgota@auburn.edu> and
238c2ecf20Sopenharmony_ci * Edmond Rodriguez <erodrig_97@yahoo.com> and
248c2ecf20Sopenharmony_ci * Aurelien Jacobs <aurel@gnuage.org>
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * The MR97311A support in gspca/mars.c has been helpful in understanding some
278c2ecf20Sopenharmony_ci * of the registers in these cameras.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MODULE_NAME "mr97310a"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "gspca.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define CAM_TYPE_CIF			0
378c2ecf20Sopenharmony_ci#define CAM_TYPE_VGA			1
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define MR97310A_BRIGHTNESS_DEFAULT	0
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define MR97310A_EXPOSURE_MIN		0
428c2ecf20Sopenharmony_ci#define MR97310A_EXPOSURE_MAX		4095
438c2ecf20Sopenharmony_ci#define MR97310A_EXPOSURE_DEFAULT	1000
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define MR97310A_GAIN_MIN		0
468c2ecf20Sopenharmony_ci#define MR97310A_GAIN_MAX		31
478c2ecf20Sopenharmony_ci#define MR97310A_GAIN_DEFAULT		25
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define MR97310A_CONTRAST_MIN		0
508c2ecf20Sopenharmony_ci#define MR97310A_CONTRAST_MAX		31
518c2ecf20Sopenharmony_ci#define MR97310A_CONTRAST_DEFAULT	23
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define MR97310A_CS_GAIN_MIN		0
548c2ecf20Sopenharmony_ci#define MR97310A_CS_GAIN_MAX		0x7ff
558c2ecf20Sopenharmony_ci#define MR97310A_CS_GAIN_DEFAULT	0x110
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
588c2ecf20Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MIN	3
598c2ecf20Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_MAX	8
608c2ecf20Sopenharmony_ci#define MR97310A_MIN_CLOCKDIV_DEFAULT	3
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,Theodore Kilgore <kilgota@auburn.edu>");
638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* global parameters */
678c2ecf20Sopenharmony_cistatic int force_sensor_type = -1;
688c2ecf20Sopenharmony_cimodule_param(force_sensor_type, int, 0644);
698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* specific webcam descriptor */
728c2ecf20Sopenharmony_cistruct sd {
738c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev;  /* !! must be the first item */
748c2ecf20Sopenharmony_ci	struct { /* exposure/min_clockdiv control cluster */
758c2ecf20Sopenharmony_ci		struct v4l2_ctrl *exposure;
768c2ecf20Sopenharmony_ci		struct v4l2_ctrl *min_clockdiv;
778c2ecf20Sopenharmony_ci	};
788c2ecf20Sopenharmony_ci	u8 sof_read;
798c2ecf20Sopenharmony_ci	u8 cam_type;	/* 0 is CIF and 1 is VGA */
808c2ecf20Sopenharmony_ci	u8 sensor_type;	/* We use 0 and 1 here, too. */
818c2ecf20Sopenharmony_ci	u8 do_lcd_stop;
828c2ecf20Sopenharmony_ci	u8 adj_colors;
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct sensor_w_data {
868c2ecf20Sopenharmony_ci	u8 reg;
878c2ecf20Sopenharmony_ci	u8 flags;
888c2ecf20Sopenharmony_ci	u8 data[16];
898c2ecf20Sopenharmony_ci	int len;
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = {
958c2ecf20Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
968c2ecf20Sopenharmony_ci		.bytesperline = 160,
978c2ecf20Sopenharmony_ci		.sizeimage = 160 * 120,
988c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
998c2ecf20Sopenharmony_ci		.priv = 4},
1008c2ecf20Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
1018c2ecf20Sopenharmony_ci		.bytesperline = 176,
1028c2ecf20Sopenharmony_ci		.sizeimage = 176 * 144,
1038c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
1048c2ecf20Sopenharmony_ci		.priv = 3},
1058c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
1068c2ecf20Sopenharmony_ci		.bytesperline = 320,
1078c2ecf20Sopenharmony_ci		.sizeimage = 320 * 240,
1088c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
1098c2ecf20Sopenharmony_ci		.priv = 2},
1108c2ecf20Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
1118c2ecf20Sopenharmony_ci		.bytesperline = 352,
1128c2ecf20Sopenharmony_ci		.sizeimage = 352 * 288,
1138c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
1148c2ecf20Sopenharmony_ci		.priv = 1},
1158c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
1168c2ecf20Sopenharmony_ci		.bytesperline = 640,
1178c2ecf20Sopenharmony_ci		.sizeimage = 640 * 480,
1188c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
1198c2ecf20Sopenharmony_ci		.priv = 0},
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* the bytes to write are in gspca_dev->usb_buf */
1238c2ecf20Sopenharmony_cistatic int mr_write(struct gspca_dev *gspca_dev, int len)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int rc;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	rc = usb_bulk_msg(gspca_dev->dev,
1288c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(gspca_dev->dev, 4),
1298c2ecf20Sopenharmony_ci			  gspca_dev->usb_buf, len, NULL, 500);
1308c2ecf20Sopenharmony_ci	if (rc < 0)
1318c2ecf20Sopenharmony_ci		pr_err("reg write [%02x] error %d\n",
1328c2ecf20Sopenharmony_ci		       gspca_dev->usb_buf[0], rc);
1338c2ecf20Sopenharmony_ci	return rc;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* the bytes are read into gspca_dev->usb_buf */
1378c2ecf20Sopenharmony_cistatic int mr_read(struct gspca_dev *gspca_dev, int len)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	int rc;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	rc = usb_bulk_msg(gspca_dev->dev,
1428c2ecf20Sopenharmony_ci			  usb_rcvbulkpipe(gspca_dev->dev, 3),
1438c2ecf20Sopenharmony_ci			  gspca_dev->usb_buf, len, NULL, 500);
1448c2ecf20Sopenharmony_ci	if (rc < 0)
1458c2ecf20Sopenharmony_ci		pr_err("reg read [%02x] error %d\n",
1468c2ecf20Sopenharmony_ci		       gspca_dev->usb_buf[0], rc);
1478c2ecf20Sopenharmony_ci	return rc;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags,
1518c2ecf20Sopenharmony_ci	const u8 *data, int len)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x1f;
1548c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = flags;
1558c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[2] = reg;
1568c2ecf20Sopenharmony_ci	memcpy(gspca_dev->usb_buf + 3, data, len);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return mr_write(gspca_dev, len + 3);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int sensor_write_regs(struct gspca_dev *gspca_dev,
1628c2ecf20Sopenharmony_ci	const struct sensor_w_data *data, int len)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int i, rc;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
1678c2ecf20Sopenharmony_ci		rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags,
1688c2ecf20Sopenharmony_ci					  data[i].data, data[i].len);
1698c2ecf20Sopenharmony_ci		if (rc < 0)
1708c2ecf20Sopenharmony_ci			return rc;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
1798c2ecf20Sopenharmony_ci	u8 buf, confirm_reg;
1808c2ecf20Sopenharmony_ci	int rc;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	buf = data;
1838c2ecf20Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF) {
1848c2ecf20Sopenharmony_ci		rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
1858c2ecf20Sopenharmony_ci		confirm_reg = sd->sensor_type ? 0x13 : 0x11;
1868c2ecf20Sopenharmony_ci	} else {
1878c2ecf20Sopenharmony_ci		rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
1888c2ecf20Sopenharmony_ci		confirm_reg = 0x11;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci	if (rc < 0)
1918c2ecf20Sopenharmony_ci		return rc;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	buf = 0x01;
1948c2ecf20Sopenharmony_ci	rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
1958c2ecf20Sopenharmony_ci	if (rc < 0)
1968c2ecf20Sopenharmony_ci		return rc;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	int err_code;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = reg;
2068c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 1);
2078c2ecf20Sopenharmony_ci	if (err_code < 0)
2088c2ecf20Sopenharmony_ci		return err_code;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	err_code = mr_read(gspca_dev, 16);
2118c2ecf20Sopenharmony_ci	if (err_code < 0)
2128c2ecf20Sopenharmony_ci		return err_code;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (verbose)
2158c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "Register: %02x reads %02x%02x%02x\n",
2168c2ecf20Sopenharmony_ci			  reg,
2178c2ecf20Sopenharmony_ci			  gspca_dev->usb_buf[0],
2188c2ecf20Sopenharmony_ci			  gspca_dev->usb_buf[1],
2198c2ecf20Sopenharmony_ci			  gspca_dev->usb_buf[2]);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int zero_the_pointer(struct gspca_dev *gspca_dev)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	__u8 *data = gspca_dev->usb_buf;
2278c2ecf20Sopenharmony_ci	int err_code;
2288c2ecf20Sopenharmony_ci	u8 status = 0;
2298c2ecf20Sopenharmony_ci	int tries = 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
2328c2ecf20Sopenharmony_ci	if (err_code < 0)
2338c2ecf20Sopenharmony_ci		return err_code;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	data[0] = 0x19;
2368c2ecf20Sopenharmony_ci	data[1] = 0x51;
2378c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
2388c2ecf20Sopenharmony_ci	if (err_code < 0)
2398c2ecf20Sopenharmony_ci		return err_code;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
2428c2ecf20Sopenharmony_ci	if (err_code < 0)
2438c2ecf20Sopenharmony_ci		return err_code;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	data[0] = 0x19;
2468c2ecf20Sopenharmony_ci	data[1] = 0xba;
2478c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
2488c2ecf20Sopenharmony_ci	if (err_code < 0)
2498c2ecf20Sopenharmony_ci		return err_code;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
2528c2ecf20Sopenharmony_ci	if (err_code < 0)
2538c2ecf20Sopenharmony_ci		return err_code;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	data[0] = 0x19;
2568c2ecf20Sopenharmony_ci	data[1] = 0x00;
2578c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
2588c2ecf20Sopenharmony_ci	if (err_code < 0)
2598c2ecf20Sopenharmony_ci		return err_code;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x21, 0);
2628c2ecf20Sopenharmony_ci	if (err_code < 0)
2638c2ecf20Sopenharmony_ci		return err_code;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	data[0] = 0x19;
2668c2ecf20Sopenharmony_ci	data[1] = 0x00;
2678c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 2);
2688c2ecf20Sopenharmony_ci	if (err_code < 0)
2698c2ecf20Sopenharmony_ci		return err_code;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	while (status != 0x0a && tries < 256) {
2728c2ecf20Sopenharmony_ci		err_code = cam_get_response16(gspca_dev, 0x21, 0);
2738c2ecf20Sopenharmony_ci		status = data[0];
2748c2ecf20Sopenharmony_ci		tries++;
2758c2ecf20Sopenharmony_ci		if (err_code < 0)
2768c2ecf20Sopenharmony_ci			return err_code;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	if (status != 0x0a)
2798c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "status is %02x\n", status);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	tries = 0;
2828c2ecf20Sopenharmony_ci	while (tries < 4) {
2838c2ecf20Sopenharmony_ci		data[0] = 0x19;
2848c2ecf20Sopenharmony_ci		data[1] = 0x00;
2858c2ecf20Sopenharmony_ci		err_code = mr_write(gspca_dev, 2);
2868c2ecf20Sopenharmony_ci		if (err_code < 0)
2878c2ecf20Sopenharmony_ci			return err_code;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		err_code = cam_get_response16(gspca_dev, 0x21, 0);
2908c2ecf20Sopenharmony_ci		tries++;
2918c2ecf20Sopenharmony_ci		if (err_code < 0)
2928c2ecf20Sopenharmony_ci			return err_code;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	data[0] = 0x19;
2968c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 1);
2978c2ecf20Sopenharmony_ci	if (err_code < 0)
2988c2ecf20Sopenharmony_ci		return err_code;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	err_code = mr_read(gspca_dev, 16);
3018c2ecf20Sopenharmony_ci	if (err_code < 0)
3028c2ecf20Sopenharmony_ci		return err_code;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int stream_start(struct gspca_dev *gspca_dev)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x01;
3108c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x01;
3118c2ecf20Sopenharmony_ci	return mr_write(gspca_dev, 2);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic void stream_stop(struct gspca_dev *gspca_dev)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x01;
3178c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x00;
3188c2ecf20Sopenharmony_ci	if (mr_write(gspca_dev, 2) < 0)
3198c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "Stream Stop failed\n");
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void lcd_stop(struct gspca_dev *gspca_dev)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x19;
3258c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x54;
3268c2ecf20Sopenharmony_ci	if (mr_write(gspca_dev, 2) < 0)
3278c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "LCD Stop failed\n");
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int isoc_enable(struct gspca_dev *gspca_dev)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = 0x00;
3338c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = 0x4d;  /* ISOC transferring enable... */
3348c2ecf20Sopenharmony_ci	return mr_write(gspca_dev, 2);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/* This function is called at probe time */
3388c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
3398c2ecf20Sopenharmony_ci		     const struct usb_device_id *id)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3428c2ecf20Sopenharmony_ci	struct cam *cam;
3438c2ecf20Sopenharmony_ci	int err_code;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	cam = &gspca_dev->cam;
3468c2ecf20Sopenharmony_ci	cam->cam_mode = vga_mode;
3478c2ecf20Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(vga_mode);
3488c2ecf20Sopenharmony_ci	sd->do_lcd_stop = 0;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* Several of the supported CIF cameras share the same USB ID but
3518c2ecf20Sopenharmony_ci	 * require different initializations and different control settings.
3528c2ecf20Sopenharmony_ci	 * The same is true of the VGA cameras. Therefore, we are forced
3538c2ecf20Sopenharmony_ci	 * to start the initialization process in order to determine which
3548c2ecf20Sopenharmony_ci	 * camera is present. Some of the supported cameras require the
3558c2ecf20Sopenharmony_ci	 * memory pointer to be set to 0 as the very first item of business
3568c2ecf20Sopenharmony_ci	 * or else they will not stream. So we do that immediately.
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	err_code = zero_the_pointer(gspca_dev);
3598c2ecf20Sopenharmony_ci	if (err_code < 0)
3608c2ecf20Sopenharmony_ci		return err_code;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	err_code = stream_start(gspca_dev);
3638c2ecf20Sopenharmony_ci	if (err_code < 0)
3648c2ecf20Sopenharmony_ci		return err_code;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* Now, the query for sensor type. */
3678c2ecf20Sopenharmony_ci	err_code = cam_get_response16(gspca_dev, 0x07, 1);
3688c2ecf20Sopenharmony_ci	if (err_code < 0)
3698c2ecf20Sopenharmony_ci		return err_code;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
3728c2ecf20Sopenharmony_ci		sd->cam_type = CAM_TYPE_CIF;
3738c2ecf20Sopenharmony_ci		cam->nmodes--;
3748c2ecf20Sopenharmony_ci		/*
3758c2ecf20Sopenharmony_ci		 * All but one of the known CIF cameras share the same USB ID,
3768c2ecf20Sopenharmony_ci		 * but two different init routines are in use, and the control
3778c2ecf20Sopenharmony_ci		 * settings are different, too. We need to detect which camera
3788c2ecf20Sopenharmony_ci		 * of the two known varieties is connected!
3798c2ecf20Sopenharmony_ci		 *
3808c2ecf20Sopenharmony_ci		 * A list of known CIF cameras follows. They all report either
3818c2ecf20Sopenharmony_ci		 * 0200 for type 0 or 0300 for type 1.
3828c2ecf20Sopenharmony_ci		 * If you have another to report, please do
3838c2ecf20Sopenharmony_ci		 *
3848c2ecf20Sopenharmony_ci		 * Name		sd->sensor_type		reported by
3858c2ecf20Sopenharmony_ci		 *
3868c2ecf20Sopenharmony_ci		 * Sakar 56379 Spy-shot	0		T. Kilgore
3878c2ecf20Sopenharmony_ci		 * Innovage		0		T. Kilgore
3888c2ecf20Sopenharmony_ci		 * Vivitar Mini		0		H. De Goede
3898c2ecf20Sopenharmony_ci		 * Vivitar Mini		0		E. Rodriguez
3908c2ecf20Sopenharmony_ci		 * Vivitar Mini		1		T. Kilgore
3918c2ecf20Sopenharmony_ci		 * Elta-Media 8212dc	1		T. Kaiser
3928c2ecf20Sopenharmony_ci		 * Philips dig. keych.	1		T. Kilgore
3938c2ecf20Sopenharmony_ci		 * Trust Spyc@m 100	1		A. Jacobs
3948c2ecf20Sopenharmony_ci		 */
3958c2ecf20Sopenharmony_ci		switch (gspca_dev->usb_buf[0]) {
3968c2ecf20Sopenharmony_ci		case 2:
3978c2ecf20Sopenharmony_ci			sd->sensor_type = 0;
3988c2ecf20Sopenharmony_ci			break;
3998c2ecf20Sopenharmony_ci		case 3:
4008c2ecf20Sopenharmony_ci			sd->sensor_type = 1;
4018c2ecf20Sopenharmony_ci			break;
4028c2ecf20Sopenharmony_ci		default:
4038c2ecf20Sopenharmony_ci			pr_err("Unknown CIF Sensor id : %02x\n",
4048c2ecf20Sopenharmony_ci			       gspca_dev->usb_buf[1]);
4058c2ecf20Sopenharmony_ci			return -ENODEV;
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "MR97310A CIF camera detected, sensor: %d\n",
4088c2ecf20Sopenharmony_ci			  sd->sensor_type);
4098c2ecf20Sopenharmony_ci	} else {
4108c2ecf20Sopenharmony_ci		sd->cam_type = CAM_TYPE_VGA;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		/*
4138c2ecf20Sopenharmony_ci		 * Here is a table of the responses to the query for sensor
4148c2ecf20Sopenharmony_ci		 * type, from the known MR97310A VGA cameras. Six different
4158c2ecf20Sopenharmony_ci		 * cameras of which five share the same USB ID.
4168c2ecf20Sopenharmony_ci		 *
4178c2ecf20Sopenharmony_ci		 * Name			gspca_dev->usb_buf[]	sd->sensor_type
4188c2ecf20Sopenharmony_ci		 *				sd->do_lcd_stop
4198c2ecf20Sopenharmony_ci		 * Aiptek Pencam VGA+	0300		0		1
4208c2ecf20Sopenharmony_ci		 * ION digital		0300		0		1
4218c2ecf20Sopenharmony_ci		 * Argus DC-1620	0450		1		0
4228c2ecf20Sopenharmony_ci		 * Argus QuickClix	0420		1		1
4238c2ecf20Sopenharmony_ci		 * Sakar 77379 Digital	0350		0		1
4248c2ecf20Sopenharmony_ci		 * Sakar 1638x CyberPix	0120		0		2
4258c2ecf20Sopenharmony_ci		 *
4268c2ecf20Sopenharmony_ci		 * Based upon these results, we assume default settings
4278c2ecf20Sopenharmony_ci		 * and then correct as necessary, as follows.
4288c2ecf20Sopenharmony_ci		 *
4298c2ecf20Sopenharmony_ci		 */
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		sd->sensor_type = 1;
4328c2ecf20Sopenharmony_ci		sd->do_lcd_stop = 0;
4338c2ecf20Sopenharmony_ci		sd->adj_colors = 0;
4348c2ecf20Sopenharmony_ci		if (gspca_dev->usb_buf[0] == 0x01) {
4358c2ecf20Sopenharmony_ci			sd->sensor_type = 2;
4368c2ecf20Sopenharmony_ci		} else if ((gspca_dev->usb_buf[0] != 0x03) &&
4378c2ecf20Sopenharmony_ci					(gspca_dev->usb_buf[0] != 0x04)) {
4388c2ecf20Sopenharmony_ci			pr_err("Unknown VGA Sensor id Byte 0: %02x\n",
4398c2ecf20Sopenharmony_ci			       gspca_dev->usb_buf[0]);
4408c2ecf20Sopenharmony_ci			pr_err("Defaults assumed, may not work\n");
4418c2ecf20Sopenharmony_ci			pr_err("Please report this\n");
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci		/* Sakar Digital color needs to be adjusted. */
4448c2ecf20Sopenharmony_ci		if ((gspca_dev->usb_buf[0] == 0x03) &&
4458c2ecf20Sopenharmony_ci					(gspca_dev->usb_buf[1] == 0x50))
4468c2ecf20Sopenharmony_ci			sd->adj_colors = 1;
4478c2ecf20Sopenharmony_ci		if (gspca_dev->usb_buf[0] == 0x04) {
4488c2ecf20Sopenharmony_ci			sd->do_lcd_stop = 1;
4498c2ecf20Sopenharmony_ci			switch (gspca_dev->usb_buf[1]) {
4508c2ecf20Sopenharmony_ci			case 0x50:
4518c2ecf20Sopenharmony_ci				sd->sensor_type = 0;
4528c2ecf20Sopenharmony_ci				gspca_dbg(gspca_dev, D_PROBE, "sensor_type corrected to 0\n");
4538c2ecf20Sopenharmony_ci				break;
4548c2ecf20Sopenharmony_ci			case 0x20:
4558c2ecf20Sopenharmony_ci				/* Nothing to do here. */
4568c2ecf20Sopenharmony_ci				break;
4578c2ecf20Sopenharmony_ci			default:
4588c2ecf20Sopenharmony_ci				pr_err("Unknown VGA Sensor id Byte 1: %02x\n",
4598c2ecf20Sopenharmony_ci				       gspca_dev->usb_buf[1]);
4608c2ecf20Sopenharmony_ci				pr_err("Defaults assumed, may not work\n");
4618c2ecf20Sopenharmony_ci				pr_err("Please report this\n");
4628c2ecf20Sopenharmony_ci			}
4638c2ecf20Sopenharmony_ci		}
4648c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "MR97310A VGA camera detected, sensor: %d\n",
4658c2ecf20Sopenharmony_ci			  sd->sensor_type);
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci	/* Stop streaming as we've started it only to probe the sensor type. */
4688c2ecf20Sopenharmony_ci	sd_stopN(gspca_dev);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (force_sensor_type != -1) {
4718c2ecf20Sopenharmony_ci		sd->sensor_type = !!force_sensor_type;
4728c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_PROBE, "Forcing sensor type to: %d\n",
4738c2ecf20Sopenharmony_ci			  sd->sensor_type);
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
4808c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	return 0;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int start_cif_cam(struct gspca_dev *gspca_dev)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4888c2ecf20Sopenharmony_ci	__u8 *data = gspca_dev->usb_buf;
4898c2ecf20Sopenharmony_ci	int err_code;
4908c2ecf20Sopenharmony_ci	static const __u8 startup_string[] = {
4918c2ecf20Sopenharmony_ci		0x00,
4928c2ecf20Sopenharmony_ci		0x0d,
4938c2ecf20Sopenharmony_ci		0x01,
4948c2ecf20Sopenharmony_ci		0x00, /* Hsize/8 for 352 or 320 */
4958c2ecf20Sopenharmony_ci		0x00, /* Vsize/4 for 288 or 240 */
4968c2ecf20Sopenharmony_ci		0x13, /* or 0xbb, depends on sensor */
4978c2ecf20Sopenharmony_ci		0x00, /* Hstart, depends on res. */
4988c2ecf20Sopenharmony_ci		0x00, /* reserved ? */
4998c2ecf20Sopenharmony_ci		0x00, /* Vstart, depends on res. and sensor */
5008c2ecf20Sopenharmony_ci		0x50, /* 0x54 to get 176 or 160 */
5018c2ecf20Sopenharmony_ci		0xc0
5028c2ecf20Sopenharmony_ci	};
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* Note: Some of the above descriptions guessed from MR97113A driver */
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	memcpy(data, startup_string, 11);
5078c2ecf20Sopenharmony_ci	if (sd->sensor_type)
5088c2ecf20Sopenharmony_ci		data[5] = 0xbb;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	switch (gspca_dev->pixfmt.width) {
5118c2ecf20Sopenharmony_ci	case 160:
5128c2ecf20Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down from 320 */
5138c2ecf20Sopenharmony_ci		fallthrough;
5148c2ecf20Sopenharmony_ci	case 320:
5158c2ecf20Sopenharmony_ci	default:
5168c2ecf20Sopenharmony_ci		data[3] = 0x28;			   /* reg 2, H size/8 */
5178c2ecf20Sopenharmony_ci		data[4] = 0x3c;			   /* reg 3, V size/4 */
5188c2ecf20Sopenharmony_ci		data[6] = 0x14;			   /* reg 5, H start  */
5198c2ecf20Sopenharmony_ci		data[8] = 0x1a + sd->sensor_type;  /* reg 7, V start  */
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	case 176:
5228c2ecf20Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down from 352 */
5238c2ecf20Sopenharmony_ci		fallthrough;
5248c2ecf20Sopenharmony_ci	case 352:
5258c2ecf20Sopenharmony_ci		data[3] = 0x2c;			   /* reg 2, H size/8 */
5268c2ecf20Sopenharmony_ci		data[4] = 0x48;			   /* reg 3, V size/4 */
5278c2ecf20Sopenharmony_ci		data[6] = 0x06;			   /* reg 5, H start  */
5288c2ecf20Sopenharmony_ci		data[8] = 0x06 - sd->sensor_type;  /* reg 7, V start  */
5298c2ecf20Sopenharmony_ci		break;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 11);
5328c2ecf20Sopenharmony_ci	if (err_code < 0)
5338c2ecf20Sopenharmony_ci		return err_code;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (!sd->sensor_type) {
5368c2ecf20Sopenharmony_ci		static const struct sensor_w_data cif_sensor0_init_data[] = {
5378c2ecf20Sopenharmony_ci			{0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01,
5388c2ecf20Sopenharmony_ci				      0x0f, 0x14, 0x0f, 0x10}, 8},
5398c2ecf20Sopenharmony_ci			{0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5},
5408c2ecf20Sopenharmony_ci			{0x12, 0x00, {0x07}, 1},
5418c2ecf20Sopenharmony_ci			{0x1f, 0x00, {0x06}, 1},
5428c2ecf20Sopenharmony_ci			{0x27, 0x00, {0x04}, 1},
5438c2ecf20Sopenharmony_ci			{0x29, 0x00, {0x0c}, 1},
5448c2ecf20Sopenharmony_ci			{0x40, 0x00, {0x40, 0x00, 0x04}, 3},
5458c2ecf20Sopenharmony_ci			{0x50, 0x00, {0x60}, 1},
5468c2ecf20Sopenharmony_ci			{0x60, 0x00, {0x06}, 1},
5478c2ecf20Sopenharmony_ci			{0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6},
5488c2ecf20Sopenharmony_ci			{0x72, 0x00, {0x1e, 0x56}, 2},
5498c2ecf20Sopenharmony_ci			{0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02,
5508c2ecf20Sopenharmony_ci				      0x31, 0x80, 0x00}, 9},
5518c2ecf20Sopenharmony_ci			{0x11, 0x00, {0x01}, 1},
5528c2ecf20Sopenharmony_ci			{0, 0, {0}, 0}
5538c2ecf20Sopenharmony_ci		};
5548c2ecf20Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data,
5558c2ecf20Sopenharmony_ci					 ARRAY_SIZE(cif_sensor0_init_data));
5568c2ecf20Sopenharmony_ci	} else {	/* sd->sensor_type = 1 */
5578c2ecf20Sopenharmony_ci		static const struct sensor_w_data cif_sensor1_init_data[] = {
5588c2ecf20Sopenharmony_ci			/* Reg 3,4, 7,8 get set by the controls */
5598c2ecf20Sopenharmony_ci			{0x02, 0x00, {0x10}, 1},
5608c2ecf20Sopenharmony_ci			{0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */
5618c2ecf20Sopenharmony_ci			{0x06, 0x01, {0x00}, 1},
5628c2ecf20Sopenharmony_ci			{0x09, 0x02, {0x0e}, 1},
5638c2ecf20Sopenharmony_ci			{0x0a, 0x02, {0x05}, 1},
5648c2ecf20Sopenharmony_ci			{0x0b, 0x02, {0x05}, 1},
5658c2ecf20Sopenharmony_ci			{0x0c, 0x02, {0x0f}, 1},
5668c2ecf20Sopenharmony_ci			{0x0d, 0x02, {0x07}, 1},
5678c2ecf20Sopenharmony_ci			{0x0e, 0x02, {0x0c}, 1},
5688c2ecf20Sopenharmony_ci			{0x0f, 0x00, {0x00}, 1},
5698c2ecf20Sopenharmony_ci			{0x10, 0x00, {0x06}, 1},
5708c2ecf20Sopenharmony_ci			{0x11, 0x00, {0x07}, 1},
5718c2ecf20Sopenharmony_ci			{0x12, 0x00, {0x00}, 1},
5728c2ecf20Sopenharmony_ci			{0x13, 0x00, {0x01}, 1},
5738c2ecf20Sopenharmony_ci			{0, 0, {0}, 0}
5748c2ecf20Sopenharmony_ci		};
5758c2ecf20Sopenharmony_ci		/* Without this command the cam won't work with USB-UHCI */
5768c2ecf20Sopenharmony_ci		gspca_dev->usb_buf[0] = 0x0a;
5778c2ecf20Sopenharmony_ci		gspca_dev->usb_buf[1] = 0x00;
5788c2ecf20Sopenharmony_ci		err_code = mr_write(gspca_dev, 2);
5798c2ecf20Sopenharmony_ci		if (err_code < 0)
5808c2ecf20Sopenharmony_ci			return err_code;
5818c2ecf20Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
5828c2ecf20Sopenharmony_ci					 ARRAY_SIZE(cif_sensor1_init_data));
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci	return err_code;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int start_vga_cam(struct gspca_dev *gspca_dev)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5908c2ecf20Sopenharmony_ci	__u8 *data = gspca_dev->usb_buf;
5918c2ecf20Sopenharmony_ci	int err_code;
5928c2ecf20Sopenharmony_ci	static const __u8 startup_string[] =
5938c2ecf20Sopenharmony_ci		{0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00,
5948c2ecf20Sopenharmony_ci		 0x00, 0x50, 0xc0};
5958c2ecf20Sopenharmony_ci	/* What some of these mean is explained in start_cif_cam(), above */
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	memcpy(data, startup_string, 11);
5988c2ecf20Sopenharmony_ci	if (!sd->sensor_type) {
5998c2ecf20Sopenharmony_ci		data[5]  = 0x00;
6008c2ecf20Sopenharmony_ci		data[10] = 0x91;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci	if (sd->sensor_type == 2) {
6038c2ecf20Sopenharmony_ci		data[5]  = 0x00;
6048c2ecf20Sopenharmony_ci		data[10] = 0x18;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	switch (gspca_dev->pixfmt.width) {
6088c2ecf20Sopenharmony_ci	case 160:
6098c2ecf20Sopenharmony_ci		data[9] |= 0x0c;  /* reg 8, 4:1 scale down */
6108c2ecf20Sopenharmony_ci		fallthrough;
6118c2ecf20Sopenharmony_ci	case 320:
6128c2ecf20Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down */
6138c2ecf20Sopenharmony_ci		fallthrough;
6148c2ecf20Sopenharmony_ci	case 640:
6158c2ecf20Sopenharmony_ci	default:
6168c2ecf20Sopenharmony_ci		data[3] = 0x50;  /* reg 2, H size/8 */
6178c2ecf20Sopenharmony_ci		data[4] = 0x78;  /* reg 3, V size/4 */
6188c2ecf20Sopenharmony_ci		data[6] = 0x04;  /* reg 5, H start */
6198c2ecf20Sopenharmony_ci		data[8] = 0x03;  /* reg 7, V start */
6208c2ecf20Sopenharmony_ci		if (sd->sensor_type == 2) {
6218c2ecf20Sopenharmony_ci			data[6] = 2;
6228c2ecf20Sopenharmony_ci			data[8] = 1;
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci		if (sd->do_lcd_stop)
6258c2ecf20Sopenharmony_ci			data[8] = 0x04;  /* Bayer tile shifted */
6268c2ecf20Sopenharmony_ci		break;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	case 176:
6298c2ecf20Sopenharmony_ci		data[9] |= 0x04;  /* reg 8, 2:1 scale down */
6308c2ecf20Sopenharmony_ci		fallthrough;
6318c2ecf20Sopenharmony_ci	case 352:
6328c2ecf20Sopenharmony_ci		data[3] = 0x2c;  /* reg 2, H size */
6338c2ecf20Sopenharmony_ci		data[4] = 0x48;  /* reg 3, V size */
6348c2ecf20Sopenharmony_ci		data[6] = 0x94;  /* reg 5, H start */
6358c2ecf20Sopenharmony_ci		data[8] = 0x63;  /* reg 7, V start */
6368c2ecf20Sopenharmony_ci		if (sd->do_lcd_stop)
6378c2ecf20Sopenharmony_ci			data[8] = 0x64;  /* Bayer tile shifted */
6388c2ecf20Sopenharmony_ci		break;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	err_code = mr_write(gspca_dev, 11);
6428c2ecf20Sopenharmony_ci	if (err_code < 0)
6438c2ecf20Sopenharmony_ci		return err_code;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (!sd->sensor_type) {
6468c2ecf20Sopenharmony_ci		static const struct sensor_w_data vga_sensor0_init_data[] = {
6478c2ecf20Sopenharmony_ci			{0x01, 0x00, {0x0c, 0x00, 0x04}, 3},
6488c2ecf20Sopenharmony_ci			{0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4},
6498c2ecf20Sopenharmony_ci			{0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4},
6508c2ecf20Sopenharmony_ci			{0x25, 0x00, {0x03, 0xa9, 0x80}, 3},
6518c2ecf20Sopenharmony_ci			{0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4},
6528c2ecf20Sopenharmony_ci			{0, 0, {0}, 0}
6538c2ecf20Sopenharmony_ci		};
6548c2ecf20Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
6558c2ecf20Sopenharmony_ci					 ARRAY_SIZE(vga_sensor0_init_data));
6568c2ecf20Sopenharmony_ci	} else if (sd->sensor_type == 1) {
6578c2ecf20Sopenharmony_ci		static const struct sensor_w_data color_adj[] = {
6588c2ecf20Sopenharmony_ci			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
6598c2ecf20Sopenharmony_ci				/* adjusted blue, green, red gain correct
6608c2ecf20Sopenharmony_ci				   too much blue from the Sakar Digital */
6618c2ecf20Sopenharmony_ci				0x05, 0x01, 0x04}, 8}
6628c2ecf20Sopenharmony_ci		};
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci		static const struct sensor_w_data color_no_adj[] = {
6658c2ecf20Sopenharmony_ci			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
6668c2ecf20Sopenharmony_ci				/* default blue, green, red gain settings */
6678c2ecf20Sopenharmony_ci				0x07, 0x00, 0x01}, 8}
6688c2ecf20Sopenharmony_ci		};
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci		static const struct sensor_w_data vga_sensor1_init_data[] = {
6718c2ecf20Sopenharmony_ci			{0x11, 0x04, {0x01}, 1},
6728c2ecf20Sopenharmony_ci			{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
6738c2ecf20Sopenharmony_ci			/* These settings may be better for some cameras */
6748c2ecf20Sopenharmony_ci			/* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
6758c2ecf20Sopenharmony_ci				0x00, 0x0a}, 7},
6768c2ecf20Sopenharmony_ci			{0x11, 0x04, {0x01}, 1},
6778c2ecf20Sopenharmony_ci			{0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
6788c2ecf20Sopenharmony_ci			{0x11, 0x04, {0x01}, 1},
6798c2ecf20Sopenharmony_ci			{0, 0, {0}, 0}
6808c2ecf20Sopenharmony_ci		};
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		if (sd->adj_colors)
6838c2ecf20Sopenharmony_ci			err_code = sensor_write_regs(gspca_dev, color_adj,
6848c2ecf20Sopenharmony_ci					 ARRAY_SIZE(color_adj));
6858c2ecf20Sopenharmony_ci		else
6868c2ecf20Sopenharmony_ci			err_code = sensor_write_regs(gspca_dev, color_no_adj,
6878c2ecf20Sopenharmony_ci					 ARRAY_SIZE(color_no_adj));
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci		if (err_code < 0)
6908c2ecf20Sopenharmony_ci			return err_code;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
6938c2ecf20Sopenharmony_ci					 ARRAY_SIZE(vga_sensor1_init_data));
6948c2ecf20Sopenharmony_ci	} else {	/* sensor type == 2 */
6958c2ecf20Sopenharmony_ci		static const struct sensor_w_data vga_sensor2_init_data[] = {
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci			{0x01, 0x00, {0x48}, 1},
6988c2ecf20Sopenharmony_ci			{0x02, 0x00, {0x22}, 1},
6998c2ecf20Sopenharmony_ci			/* Reg 3 msb and 4 is lsb of the exposure setting*/
7008c2ecf20Sopenharmony_ci			{0x05, 0x00, {0x10}, 1},
7018c2ecf20Sopenharmony_ci			{0x06, 0x00, {0x00}, 1},
7028c2ecf20Sopenharmony_ci			{0x07, 0x00, {0x00}, 1},
7038c2ecf20Sopenharmony_ci			{0x08, 0x00, {0x00}, 1},
7048c2ecf20Sopenharmony_ci			{0x09, 0x00, {0x00}, 1},
7058c2ecf20Sopenharmony_ci			/* The following are used in the gain control
7068c2ecf20Sopenharmony_ci			 * which is BTW completely borked in the OEM driver
7078c2ecf20Sopenharmony_ci			 * The values for each color go from 0 to 0x7ff
7088c2ecf20Sopenharmony_ci			 *{0x0a, 0x00, {0x01}, 1},  green1 gain msb
7098c2ecf20Sopenharmony_ci			 *{0x0b, 0x00, {0x10}, 1},  green1 gain lsb
7108c2ecf20Sopenharmony_ci			 *{0x0c, 0x00, {0x01}, 1},  red gain msb
7118c2ecf20Sopenharmony_ci			 *{0x0d, 0x00, {0x10}, 1},  red gain lsb
7128c2ecf20Sopenharmony_ci			 *{0x0e, 0x00, {0x01}, 1},  blue gain msb
7138c2ecf20Sopenharmony_ci			 *{0x0f, 0x00, {0x10}, 1},  blue gain lsb
7148c2ecf20Sopenharmony_ci			 *{0x10, 0x00, {0x01}, 1}, green2 gain msb
7158c2ecf20Sopenharmony_ci			 *{0x11, 0x00, {0x10}, 1}, green2 gain lsb
7168c2ecf20Sopenharmony_ci			 */
7178c2ecf20Sopenharmony_ci			{0x12, 0x00, {0x00}, 1},
7188c2ecf20Sopenharmony_ci			{0x13, 0x00, {0x04}, 1}, /* weird effect on colors */
7198c2ecf20Sopenharmony_ci			{0x14, 0x00, {0x00}, 1},
7208c2ecf20Sopenharmony_ci			{0x15, 0x00, {0x06}, 1},
7218c2ecf20Sopenharmony_ci			{0x16, 0x00, {0x01}, 1},
7228c2ecf20Sopenharmony_ci			{0x17, 0x00, {0xe2}, 1}, /* vertical alignment */
7238c2ecf20Sopenharmony_ci			{0x18, 0x00, {0x02}, 1},
7248c2ecf20Sopenharmony_ci			{0x19, 0x00, {0x82}, 1}, /* don't mess with */
7258c2ecf20Sopenharmony_ci			{0x1a, 0x00, {0x00}, 1},
7268c2ecf20Sopenharmony_ci			{0x1b, 0x00, {0x20}, 1},
7278c2ecf20Sopenharmony_ci			/* {0x1c, 0x00, {0x17}, 1}, contrast control */
7288c2ecf20Sopenharmony_ci			{0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */
7298c2ecf20Sopenharmony_ci			{0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */
7308c2ecf20Sopenharmony_ci			{0x1f, 0x00, {0x0c}, 1},
7318c2ecf20Sopenharmony_ci			{0x20, 0x00, {0x00}, 1},
7328c2ecf20Sopenharmony_ci			{0, 0, {0}, 0}
7338c2ecf20Sopenharmony_ci		};
7348c2ecf20Sopenharmony_ci		err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data,
7358c2ecf20Sopenharmony_ci					 ARRAY_SIZE(vga_sensor2_init_data));
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci	return err_code;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7438c2ecf20Sopenharmony_ci	int err_code;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	sd->sof_read = 0;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	/* Some of the VGA cameras require the memory pointer
7488c2ecf20Sopenharmony_ci	 * to be set to 0 again. We have been forced to start the
7498c2ecf20Sopenharmony_ci	 * stream in sd_config() to detect the hardware, and closed it.
7508c2ecf20Sopenharmony_ci	 * Thus, we need here to do a completely fresh and clean start. */
7518c2ecf20Sopenharmony_ci	err_code = zero_the_pointer(gspca_dev);
7528c2ecf20Sopenharmony_ci	if (err_code < 0)
7538c2ecf20Sopenharmony_ci		return err_code;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	err_code = stream_start(gspca_dev);
7568c2ecf20Sopenharmony_ci	if (err_code < 0)
7578c2ecf20Sopenharmony_ci		return err_code;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF) {
7608c2ecf20Sopenharmony_ci		err_code = start_cif_cam(gspca_dev);
7618c2ecf20Sopenharmony_ci	} else {
7628c2ecf20Sopenharmony_ci		err_code = start_vga_cam(gspca_dev);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci	if (err_code < 0)
7658c2ecf20Sopenharmony_ci		return err_code;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return isoc_enable(gspca_dev);
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	stream_stop(gspca_dev);
7758c2ecf20Sopenharmony_ci	/* Not all the cams need this, but even if not, probably a good idea */
7768c2ecf20Sopenharmony_ci	zero_the_pointer(gspca_dev);
7778c2ecf20Sopenharmony_ci	if (sd->do_lcd_stop)
7788c2ecf20Sopenharmony_ci		lcd_stop(gspca_dev);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7848c2ecf20Sopenharmony_ci	u8 sign_reg = 7;  /* This reg and the next one used on CIF cams. */
7858c2ecf20Sopenharmony_ci	u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
7868c2ecf20Sopenharmony_ci	static const u8 quick_clix_table[] =
7878c2ecf20Sopenharmony_ci	/*	  0  1  2   3  4  5  6  7  8  9  10  11  12  13  14  15 */
7888c2ecf20Sopenharmony_ci		{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9,  7, 10, 13, 11, 14, 15};
7898c2ecf20Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_VGA) {
7908c2ecf20Sopenharmony_ci		sign_reg += 4;
7918c2ecf20Sopenharmony_ci		value_reg += 4;
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
7958c2ecf20Sopenharmony_ci	if (val > 0) {
7968c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, sign_reg, 0x00);
7978c2ecf20Sopenharmony_ci	} else {
7988c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, sign_reg, 0x01);
7998c2ecf20Sopenharmony_ci		val = 257 - val;
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci	/* Use lookup table for funky Argus QuickClix brightness */
8028c2ecf20Sopenharmony_ci	if (sd->do_lcd_stop)
8038c2ecf20Sopenharmony_ci		val = quick_clix_table[val];
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	sensor_write1(gspca_dev, value_reg, val);
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8118c2ecf20Sopenharmony_ci	int exposure = MR97310A_EXPOSURE_DEFAULT;
8128c2ecf20Sopenharmony_ci	u8 buf[2];
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
8158c2ecf20Sopenharmony_ci		/* This cam does not like exposure settings < 300,
8168c2ecf20Sopenharmony_ci		   so scale 0 - 4095 to 300 - 4095 */
8178c2ecf20Sopenharmony_ci		exposure = (expo * 9267) / 10000 + 300;
8188c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 3, exposure >> 4);
8198c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 4, exposure & 0x0f);
8208c2ecf20Sopenharmony_ci	} else if (sd->sensor_type == 2) {
8218c2ecf20Sopenharmony_ci		exposure = expo;
8228c2ecf20Sopenharmony_ci		exposure >>= 3;
8238c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 3, exposure >> 8);
8248c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 4, exposure & 0xff);
8258c2ecf20Sopenharmony_ci	} else {
8268c2ecf20Sopenharmony_ci		/* We have both a clock divider and an exposure register.
8278c2ecf20Sopenharmony_ci		   We first calculate the clock divider, as that determines
8288c2ecf20Sopenharmony_ci		   the maximum exposure and then we calculate the exposure
8298c2ecf20Sopenharmony_ci		   register setting (which goes from 0 - 511).
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		   Note our 0 - 4095 exposure is mapped to 0 - 511
8328c2ecf20Sopenharmony_ci		   milliseconds exposure time */
8338c2ecf20Sopenharmony_ci		u8 clockdiv = (60 * expo + 7999) / 8000;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci		/* Limit framerate to not exceed usb bandwidth */
8368c2ecf20Sopenharmony_ci		if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320)
8378c2ecf20Sopenharmony_ci			clockdiv = min_clockdiv;
8388c2ecf20Sopenharmony_ci		else if (clockdiv < 2)
8398c2ecf20Sopenharmony_ci			clockdiv = 2;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
8428c2ecf20Sopenharmony_ci			clockdiv = 4;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
8458c2ecf20Sopenharmony_ci		exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
8468c2ecf20Sopenharmony_ci		exposure = (60 * 511 * expo) / (8000 * clockdiv);
8478c2ecf20Sopenharmony_ci		if (exposure > 511)
8488c2ecf20Sopenharmony_ci			exposure = 511;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		/* exposure register value is reversed! */
8518c2ecf20Sopenharmony_ci		exposure = 511 - exposure;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		buf[0] = exposure & 0xff;
8548c2ecf20Sopenharmony_ci		buf[1] = exposure >> 8;
8558c2ecf20Sopenharmony_ci		sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
8568c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 0x02, clockdiv);
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8638c2ecf20Sopenharmony_ci	u8 gainreg;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
8668c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 0x0e, val);
8678c2ecf20Sopenharmony_ci	else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
8688c2ecf20Sopenharmony_ci		for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
8698c2ecf20Sopenharmony_ci			sensor_write1(gspca_dev, gainreg, val >> 8);
8708c2ecf20Sopenharmony_ci			sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
8718c2ecf20Sopenharmony_ci		}
8728c2ecf20Sopenharmony_ci	else
8738c2ecf20Sopenharmony_ci		sensor_write1(gspca_dev, 0x10, val);
8748c2ecf20Sopenharmony_ci}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	sensor_write1(gspca_dev, 0x1c, val);
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
8848c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
8858c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	gspca_dev->usb_err = 0;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming)
8908c2ecf20Sopenharmony_ci		return 0;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	switch (ctrl->id) {
8938c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
8948c2ecf20Sopenharmony_ci		setbrightness(gspca_dev, ctrl->val);
8958c2ecf20Sopenharmony_ci		break;
8968c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
8978c2ecf20Sopenharmony_ci		setcontrast(gspca_dev, ctrl->val);
8988c2ecf20Sopenharmony_ci		break;
8998c2ecf20Sopenharmony_ci	case V4L2_CID_EXPOSURE:
9008c2ecf20Sopenharmony_ci		setexposure(gspca_dev, sd->exposure->val,
9018c2ecf20Sopenharmony_ci			    sd->min_clockdiv ? sd->min_clockdiv->val : 0);
9028c2ecf20Sopenharmony_ci		break;
9038c2ecf20Sopenharmony_ci	case V4L2_CID_GAIN:
9048c2ecf20Sopenharmony_ci		setgain(gspca_dev, ctrl->val);
9058c2ecf20Sopenharmony_ci		break;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
9118c2ecf20Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
9128c2ecf20Sopenharmony_ci};
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
9178c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
9188c2ecf20Sopenharmony_ci	static const struct v4l2_ctrl_config clockdiv = {
9198c2ecf20Sopenharmony_ci		.ops = &sd_ctrl_ops,
9208c2ecf20Sopenharmony_ci		.id = MR97310A_CID_CLOCKDIV,
9218c2ecf20Sopenharmony_ci		.type = V4L2_CTRL_TYPE_INTEGER,
9228c2ecf20Sopenharmony_ci		.name = "Minimum Clock Divider",
9238c2ecf20Sopenharmony_ci		.min = MR97310A_MIN_CLOCKDIV_MIN,
9248c2ecf20Sopenharmony_ci		.max = MR97310A_MIN_CLOCKDIV_MAX,
9258c2ecf20Sopenharmony_ci		.step = 1,
9268c2ecf20Sopenharmony_ci		.def = MR97310A_MIN_CLOCKDIV_DEFAULT,
9278c2ecf20Sopenharmony_ci	};
9288c2ecf20Sopenharmony_ci	bool has_brightness = false;
9298c2ecf20Sopenharmony_ci	bool has_argus_brightness = false;
9308c2ecf20Sopenharmony_ci	bool has_contrast = false;
9318c2ecf20Sopenharmony_ci	bool has_gain = false;
9328c2ecf20Sopenharmony_ci	bool has_cs_gain = false;
9338c2ecf20Sopenharmony_ci	bool has_exposure = false;
9348c2ecf20Sopenharmony_ci	bool has_clockdiv = false;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
9378c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 4);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* Setup controls depending on camera type */
9408c2ecf20Sopenharmony_ci	if (sd->cam_type == CAM_TYPE_CIF) {
9418c2ecf20Sopenharmony_ci		/* No brightness for sensor_type 0 */
9428c2ecf20Sopenharmony_ci		if (sd->sensor_type == 0)
9438c2ecf20Sopenharmony_ci			has_exposure = has_gain = has_clockdiv = true;
9448c2ecf20Sopenharmony_ci		else
9458c2ecf20Sopenharmony_ci			has_exposure = has_gain = has_brightness = true;
9468c2ecf20Sopenharmony_ci	} else {
9478c2ecf20Sopenharmony_ci		/* All controls need to be disabled if VGA sensor_type is 0 */
9488c2ecf20Sopenharmony_ci		if (sd->sensor_type == 0)
9498c2ecf20Sopenharmony_ci			; /* no controls! */
9508c2ecf20Sopenharmony_ci		else if (sd->sensor_type == 2)
9518c2ecf20Sopenharmony_ci			has_exposure = has_cs_gain = has_contrast = true;
9528c2ecf20Sopenharmony_ci		else if (sd->do_lcd_stop)
9538c2ecf20Sopenharmony_ci			has_exposure = has_gain = has_argus_brightness =
9548c2ecf20Sopenharmony_ci				has_clockdiv = true;
9558c2ecf20Sopenharmony_ci		else
9568c2ecf20Sopenharmony_ci			has_exposure = has_gain = has_brightness =
9578c2ecf20Sopenharmony_ci				has_clockdiv = true;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	/* Separate brightness control description for Argus QuickClix as it has
9618c2ecf20Sopenharmony_ci	 * different limits from the other mr97310a cameras, and separate gain
9628c2ecf20Sopenharmony_ci	 * control for Sakar CyberPix camera. */
9638c2ecf20Sopenharmony_ci	/*
9648c2ecf20Sopenharmony_ci	 * This control is disabled for CIF type 1 and VGA type 0 cameras.
9658c2ecf20Sopenharmony_ci	 * It does not quite act linearly for the Argus QuickClix camera,
9668c2ecf20Sopenharmony_ci	 * but it does control brightness. The values are 0 - 15 only, and
9678c2ecf20Sopenharmony_ci	 * the table above makes them act consecutively.
9688c2ecf20Sopenharmony_ci	 */
9698c2ecf20Sopenharmony_ci	if (has_brightness)
9708c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9718c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, -254, 255, 1,
9728c2ecf20Sopenharmony_ci			MR97310A_BRIGHTNESS_DEFAULT);
9738c2ecf20Sopenharmony_ci	else if (has_argus_brightness)
9748c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9758c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 15, 1,
9768c2ecf20Sopenharmony_ci			MR97310A_BRIGHTNESS_DEFAULT);
9778c2ecf20Sopenharmony_ci	if (has_contrast)
9788c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9798c2ecf20Sopenharmony_ci			V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
9808c2ecf20Sopenharmony_ci			MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
9818c2ecf20Sopenharmony_ci	if (has_gain)
9828c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9838c2ecf20Sopenharmony_ci			V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
9848c2ecf20Sopenharmony_ci			1, MR97310A_GAIN_DEFAULT);
9858c2ecf20Sopenharmony_ci	else if (has_cs_gain)
9868c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
9878c2ecf20Sopenharmony_ci			MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
9888c2ecf20Sopenharmony_ci			1, MR97310A_CS_GAIN_DEFAULT);
9898c2ecf20Sopenharmony_ci	if (has_exposure)
9908c2ecf20Sopenharmony_ci		sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9918c2ecf20Sopenharmony_ci			V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
9928c2ecf20Sopenharmony_ci			MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
9938c2ecf20Sopenharmony_ci	if (has_clockdiv)
9948c2ecf20Sopenharmony_ci		sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (hdl->error) {
9978c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
9988c2ecf20Sopenharmony_ci		return hdl->error;
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci	if (has_exposure && has_clockdiv)
10018c2ecf20Sopenharmony_ci		v4l2_ctrl_cluster(2, &sd->exposure);
10028c2ecf20Sopenharmony_ci	return 0;
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci/* Include pac common sof detection functions */
10068c2ecf20Sopenharmony_ci#include "pac_common.h"
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
10098c2ecf20Sopenharmony_ci			u8 *data,		/* isoc packet */
10108c2ecf20Sopenharmony_ci			int len)		/* iso packet length */
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
10138c2ecf20Sopenharmony_ci	unsigned char *sof;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
10168c2ecf20Sopenharmony_ci	if (sof) {
10178c2ecf20Sopenharmony_ci		int n;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		/* finish decoding current frame */
10208c2ecf20Sopenharmony_ci		n = sof - data;
10218c2ecf20Sopenharmony_ci		if (n > sizeof pac_sof_marker)
10228c2ecf20Sopenharmony_ci			n -= sizeof pac_sof_marker;
10238c2ecf20Sopenharmony_ci		else
10248c2ecf20Sopenharmony_ci			n = 0;
10258c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET,
10268c2ecf20Sopenharmony_ci					data, n);
10278c2ecf20Sopenharmony_ci		/* Start next frame. */
10288c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET,
10298c2ecf20Sopenharmony_ci			pac_sof_marker, sizeof pac_sof_marker);
10308c2ecf20Sopenharmony_ci		len -= sof - data;
10318c2ecf20Sopenharmony_ci		data = sof;
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
10348c2ecf20Sopenharmony_ci}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci/* sub-driver description */
10378c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
10388c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
10398c2ecf20Sopenharmony_ci	.config = sd_config,
10408c2ecf20Sopenharmony_ci	.init = sd_init,
10418c2ecf20Sopenharmony_ci	.init_controls = sd_init_controls,
10428c2ecf20Sopenharmony_ci	.start = sd_start,
10438c2ecf20Sopenharmony_ci	.stopN = sd_stopN,
10448c2ecf20Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
10458c2ecf20Sopenharmony_ci};
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci/* -- module initialisation -- */
10488c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
10498c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x0110)},	/* Trust Spyc@m 100 */
10508c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x0111)},	/* Aiptek Pencam VGA+ */
10518c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x010f)},	/* All other known MR97310A VGA cams */
10528c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x010e)},	/* All known MR97310A CIF cams */
10538c2ecf20Sopenharmony_ci	{}
10548c2ecf20Sopenharmony_ci};
10558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/* -- device connect -- */
10588c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
10598c2ecf20Sopenharmony_ci		    const struct usb_device_id *id)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
10628c2ecf20Sopenharmony_ci			       THIS_MODULE);
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
10668c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
10678c2ecf20Sopenharmony_ci	.id_table = device_table,
10688c2ecf20Sopenharmony_ci	.probe = sd_probe,
10698c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
10708c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
10718c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
10728c2ecf20Sopenharmony_ci	.resume = gspca_resume,
10738c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
10748c2ecf20Sopenharmony_ci#endif
10758c2ecf20Sopenharmony_ci};
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
1078