18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *		Sunplus spca504(abc) spca533 spca536 library
48c2ecf20Sopenharmony_ci *		Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define MODULE_NAME "sunplus"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "gspca.h"
148c2ecf20Sopenharmony_ci#include "jpeg.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define QUALITY 85
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* specific webcam descriptor */
238c2ecf20Sopenharmony_cistruct sd {
248c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev;	/* !! must be the first item */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	bool autogain;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	u8 bridge;
298c2ecf20Sopenharmony_ci#define BRIDGE_SPCA504 0
308c2ecf20Sopenharmony_ci#define BRIDGE_SPCA504B 1
318c2ecf20Sopenharmony_ci#define BRIDGE_SPCA504C 2
328c2ecf20Sopenharmony_ci#define BRIDGE_SPCA533 3
338c2ecf20Sopenharmony_ci#define BRIDGE_SPCA536 4
348c2ecf20Sopenharmony_ci	u8 subtype;
358c2ecf20Sopenharmony_ci#define AiptekMiniPenCam13 1
368c2ecf20Sopenharmony_ci#define LogitechClickSmart420 2
378c2ecf20Sopenharmony_ci#define LogitechClickSmart820 3
388c2ecf20Sopenharmony_ci#define MegapixV4 4
398c2ecf20Sopenharmony_ci#define MegaImageVI 5
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	u8 jpeg_hdr[JPEG_HDR_SZ];
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = {
458c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
468c2ecf20Sopenharmony_ci		.bytesperline = 320,
478c2ecf20Sopenharmony_ci		.sizeimage = 320 * 240 * 3 / 8 + 590,
488c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
498c2ecf20Sopenharmony_ci		.priv = 2},
508c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
518c2ecf20Sopenharmony_ci		.bytesperline = 640,
528c2ecf20Sopenharmony_ci		.sizeimage = 640 * 480 * 3 / 8 + 590,
538c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
548c2ecf20Sopenharmony_ci		.priv = 1},
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format custom_mode[] = {
588c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
598c2ecf20Sopenharmony_ci		.bytesperline = 320,
608c2ecf20Sopenharmony_ci		.sizeimage = 320 * 240 * 3 / 8 + 590,
618c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
628c2ecf20Sopenharmony_ci		.priv = 2},
638c2ecf20Sopenharmony_ci	{464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
648c2ecf20Sopenharmony_ci		.bytesperline = 464,
658c2ecf20Sopenharmony_ci		.sizeimage = 464 * 480 * 3 / 8 + 590,
668c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
678c2ecf20Sopenharmony_ci		.priv = 1},
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode2[] = {
718c2ecf20Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
728c2ecf20Sopenharmony_ci		.bytesperline = 176,
738c2ecf20Sopenharmony_ci		.sizeimage = 176 * 144 * 3 / 8 + 590,
748c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
758c2ecf20Sopenharmony_ci		.priv = 4},
768c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
778c2ecf20Sopenharmony_ci		.bytesperline = 320,
788c2ecf20Sopenharmony_ci		.sizeimage = 320 * 240 * 3 / 8 + 590,
798c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
808c2ecf20Sopenharmony_ci		.priv = 3},
818c2ecf20Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
828c2ecf20Sopenharmony_ci		.bytesperline = 352,
838c2ecf20Sopenharmony_ci		.sizeimage = 352 * 288 * 3 / 8 + 590,
848c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
858c2ecf20Sopenharmony_ci		.priv = 2},
868c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
878c2ecf20Sopenharmony_ci		.bytesperline = 640,
888c2ecf20Sopenharmony_ci		.sizeimage = 640 * 480 * 3 / 8 + 590,
898c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
908c2ecf20Sopenharmony_ci		.priv = 1},
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define SPCA50X_OFFSET_DATA 10
948c2ecf20Sopenharmony_ci#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3
958c2ecf20Sopenharmony_ci#define SPCA504_PCCAM600_OFFSET_COMPRESS 4
968c2ecf20Sopenharmony_ci#define SPCA504_PCCAM600_OFFSET_MODE	 5
978c2ecf20Sopenharmony_ci#define SPCA504_PCCAM600_OFFSET_DATA	 14
988c2ecf20Sopenharmony_ci /* Frame packet header offsets for the spca533 */
998c2ecf20Sopenharmony_ci#define SPCA533_OFFSET_DATA	16
1008c2ecf20Sopenharmony_ci#define SPCA533_OFFSET_FRAMSEQ	15
1018c2ecf20Sopenharmony_ci/* Frame packet header offsets for the spca536 */
1028c2ecf20Sopenharmony_ci#define SPCA536_OFFSET_DATA	4
1038c2ecf20Sopenharmony_ci#define SPCA536_OFFSET_FRAMSEQ	1
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistruct cmd {
1068c2ecf20Sopenharmony_ci	u8 req;
1078c2ecf20Sopenharmony_ci	u16 val;
1088c2ecf20Sopenharmony_ci	u16 idx;
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Initialisation data for the Creative PC-CAM 600 */
1128c2ecf20Sopenharmony_cistatic const struct cmd spca504_pccam600_init_data[] = {
1138c2ecf20Sopenharmony_ci/*	{0xa0, 0x0000, 0x0503},  * capture mode */
1148c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x2000},
1158c2ecf20Sopenharmony_ci	{0x00, 0x0013, 0x2301},
1168c2ecf20Sopenharmony_ci	{0x00, 0x0003, 0x2000},
1178c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x21ac},
1188c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x21a6},
1198c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x21a7},	/* brightness */
1208c2ecf20Sopenharmony_ci	{0x00, 0x0020, 0x21a8},	/* contrast */
1218c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x21ac},	/* sat/hue */
1228c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x21ad},	/* hue */
1238c2ecf20Sopenharmony_ci	{0x00, 0x001a, 0x21ae},	/* saturation */
1248c2ecf20Sopenharmony_ci	{0x00, 0x0002, 0x21a3},	/* gamma */
1258c2ecf20Sopenharmony_ci	{0x30, 0x0154, 0x0008},
1268c2ecf20Sopenharmony_ci	{0x30, 0x0004, 0x0006},
1278c2ecf20Sopenharmony_ci	{0x30, 0x0258, 0x0009},
1288c2ecf20Sopenharmony_ci	{0x30, 0x0004, 0x0000},
1298c2ecf20Sopenharmony_ci	{0x30, 0x0093, 0x0004},
1308c2ecf20Sopenharmony_ci	{0x30, 0x0066, 0x0005},
1318c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x2000},
1328c2ecf20Sopenharmony_ci	{0x00, 0x0013, 0x2301},
1338c2ecf20Sopenharmony_ci	{0x00, 0x0003, 0x2000},
1348c2ecf20Sopenharmony_ci	{0x00, 0x0013, 0x2301},
1358c2ecf20Sopenharmony_ci	{0x00, 0x0003, 0x2000},
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* Creative PC-CAM 600 specific open data, sent before using the
1398c2ecf20Sopenharmony_ci * generic initialisation data from spca504_open_data.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic const struct cmd spca504_pccam600_open_data[] = {
1428c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x2501},
1438c2ecf20Sopenharmony_ci	{0x20, 0x0500, 0x0001},	/* snapshot mode */
1448c2ecf20Sopenharmony_ci	{0x00, 0x0003, 0x2880},
1458c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x2881},
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* Initialisation data for the logitech clicksmart 420 */
1498c2ecf20Sopenharmony_cistatic const struct cmd spca504A_clicksmart420_init_data[] = {
1508c2ecf20Sopenharmony_ci/*	{0xa0, 0x0000, 0x0503},  * capture mode */
1518c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x2000},
1528c2ecf20Sopenharmony_ci	{0x00, 0x0013, 0x2301},
1538c2ecf20Sopenharmony_ci	{0x00, 0x0003, 0x2000},
1548c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x21ac},
1558c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x21a6},
1568c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x21a7},	/* brightness */
1578c2ecf20Sopenharmony_ci	{0x00, 0x0020, 0x21a8},	/* contrast */
1588c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x21ac},	/* sat/hue */
1598c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x21ad},	/* hue */
1608c2ecf20Sopenharmony_ci	{0x00, 0x001a, 0x21ae},	/* saturation */
1618c2ecf20Sopenharmony_ci	{0x00, 0x0002, 0x21a3},	/* gamma */
1628c2ecf20Sopenharmony_ci	{0x30, 0x0004, 0x000a},
1638c2ecf20Sopenharmony_ci	{0xb0, 0x0001, 0x0000},
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	{0xa1, 0x0080, 0x0001},
1668c2ecf20Sopenharmony_ci	{0x30, 0x0049, 0x0000},
1678c2ecf20Sopenharmony_ci	{0x30, 0x0060, 0x0005},
1688c2ecf20Sopenharmony_ci	{0x0c, 0x0004, 0x0000},
1698c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x0000},
1708c2ecf20Sopenharmony_ci	{0x00, 0x0000, 0x2000},
1718c2ecf20Sopenharmony_ci	{0x00, 0x0013, 0x2301},
1728c2ecf20Sopenharmony_ci	{0x00, 0x0003, 0x2000},
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* clicksmart 420 open data ? */
1768c2ecf20Sopenharmony_cistatic const struct cmd spca504A_clicksmart420_open_data[] = {
1778c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x2501},
1788c2ecf20Sopenharmony_ci	{0x20, 0x0502, 0x0000},
1798c2ecf20Sopenharmony_ci	{0x06, 0x0000, 0x0000},
1808c2ecf20Sopenharmony_ci	{0x00, 0x0004, 0x2880},
1818c2ecf20Sopenharmony_ci	{0x00, 0x0001, 0x2881},
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	{0xa0, 0x0000, 0x0503},
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic const u8 qtable_creative_pccam[2][64] = {
1878c2ecf20Sopenharmony_ci	{				/* Q-table Y-components */
1888c2ecf20Sopenharmony_ci	 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
1898c2ecf20Sopenharmony_ci	 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
1908c2ecf20Sopenharmony_ci	 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
1918c2ecf20Sopenharmony_ci	 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
1928c2ecf20Sopenharmony_ci	 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
1938c2ecf20Sopenharmony_ci	 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
1948c2ecf20Sopenharmony_ci	 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
1958c2ecf20Sopenharmony_ci	 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},
1968c2ecf20Sopenharmony_ci	{				/* Q-table C-components */
1978c2ecf20Sopenharmony_ci	 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
1988c2ecf20Sopenharmony_ci	 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
1998c2ecf20Sopenharmony_ci	 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2008c2ecf20Sopenharmony_ci	 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2018c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2028c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2038c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2048c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* FIXME: This Q-table is identical to the Creative PC-CAM one,
2088c2ecf20Sopenharmony_ci *		except for one byte. Possibly a typo?
2098c2ecf20Sopenharmony_ci *		NWG: 18/05/2003.
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cistatic const u8 qtable_spca504_default[2][64] = {
2128c2ecf20Sopenharmony_ci	{				/* Q-table Y-components */
2138c2ecf20Sopenharmony_ci	 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
2148c2ecf20Sopenharmony_ci	 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
2158c2ecf20Sopenharmony_ci	 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
2168c2ecf20Sopenharmony_ci	 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
2178c2ecf20Sopenharmony_ci	 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
2188c2ecf20Sopenharmony_ci	 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
2198c2ecf20Sopenharmony_ci	 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
2208c2ecf20Sopenharmony_ci	 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e,
2218c2ecf20Sopenharmony_ci	 },
2228c2ecf20Sopenharmony_ci	{				/* Q-table C-components */
2238c2ecf20Sopenharmony_ci	 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
2248c2ecf20Sopenharmony_ci	 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
2258c2ecf20Sopenharmony_ci	 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2268c2ecf20Sopenharmony_ci	 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2278c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2288c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2298c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
2308c2ecf20Sopenharmony_ci	 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci/* read <len> bytes to gspca_dev->usb_buf */
2348c2ecf20Sopenharmony_cistatic void reg_r(struct gspca_dev *gspca_dev,
2358c2ecf20Sopenharmony_ci		  u8 req,
2368c2ecf20Sopenharmony_ci		  u16 index,
2378c2ecf20Sopenharmony_ci		  u16 len)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int ret;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (len > USB_BUF_SZ) {
2428c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "reg_r: buffer overflow\n");
2438c2ecf20Sopenharmony_ci		return;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci	if (len == 0) {
2468c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "reg_r: zero-length read\n");
2478c2ecf20Sopenharmony_ci		return;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
2508c2ecf20Sopenharmony_ci		return;
2518c2ecf20Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
2528c2ecf20Sopenharmony_ci			usb_rcvctrlpipe(gspca_dev->dev, 0),
2538c2ecf20Sopenharmony_ci			req,
2548c2ecf20Sopenharmony_ci			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2558c2ecf20Sopenharmony_ci			0,		/* value */
2568c2ecf20Sopenharmony_ci			index,
2578c2ecf20Sopenharmony_ci			gspca_dev->usb_buf, len,
2588c2ecf20Sopenharmony_ci			500);
2598c2ecf20Sopenharmony_ci	if (ret < 0) {
2608c2ecf20Sopenharmony_ci		pr_err("reg_r err %d\n", ret);
2618c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
2628c2ecf20Sopenharmony_ci		/*
2638c2ecf20Sopenharmony_ci		 * Make sure the buffer is zeroed to avoid uninitialized
2648c2ecf20Sopenharmony_ci		 * values.
2658c2ecf20Sopenharmony_ci		 */
2668c2ecf20Sopenharmony_ci		memset(gspca_dev->usb_buf, 0, USB_BUF_SZ);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/* write one byte */
2718c2ecf20Sopenharmony_cistatic void reg_w_1(struct gspca_dev *gspca_dev,
2728c2ecf20Sopenharmony_ci		   u8 req,
2738c2ecf20Sopenharmony_ci		   u16 value,
2748c2ecf20Sopenharmony_ci		   u16 index,
2758c2ecf20Sopenharmony_ci		   u16 byte)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	int ret;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
2808c2ecf20Sopenharmony_ci		return;
2818c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = byte;
2828c2ecf20Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
2838c2ecf20Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
2848c2ecf20Sopenharmony_ci			req,
2858c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2868c2ecf20Sopenharmony_ci			value, index,
2878c2ecf20Sopenharmony_ci			gspca_dev->usb_buf, 1,
2888c2ecf20Sopenharmony_ci			500);
2898c2ecf20Sopenharmony_ci	if (ret < 0) {
2908c2ecf20Sopenharmony_ci		pr_err("reg_w_1 err %d\n", ret);
2918c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/* write req / index / value */
2968c2ecf20Sopenharmony_cistatic void reg_w_riv(struct gspca_dev *gspca_dev,
2978c2ecf20Sopenharmony_ci		     u8 req, u16 index, u16 value)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
3008c2ecf20Sopenharmony_ci	int ret;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
3038c2ecf20Sopenharmony_ci		return;
3048c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev,
3058c2ecf20Sopenharmony_ci			usb_sndctrlpipe(dev, 0),
3068c2ecf20Sopenharmony_ci			req,
3078c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3088c2ecf20Sopenharmony_ci			value, index, NULL, 0, 500);
3098c2ecf20Sopenharmony_ci	if (ret < 0) {
3108c2ecf20Sopenharmony_ci		pr_err("reg_w_riv err %d\n", ret);
3118c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x\n",
3158c2ecf20Sopenharmony_ci		  req, index, value);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void write_vector(struct gspca_dev *gspca_dev,
3198c2ecf20Sopenharmony_ci			const struct cmd *data, int ncmds)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	while (--ncmds >= 0) {
3228c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, data->req, data->idx, data->val);
3238c2ecf20Sopenharmony_ci		data++;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic void setup_qtable(struct gspca_dev *gspca_dev,
3288c2ecf20Sopenharmony_ci			const u8 qtable[2][64])
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	int i;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* loop over y components */
3338c2ecf20Sopenharmony_ci	for (i = 0; i < 64; i++)
3348c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* loop over c components */
3378c2ecf20Sopenharmony_ci	for (i = 0; i < 64; i++)
3388c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
3428c2ecf20Sopenharmony_ci			     u8 req, u16 idx, u16 val)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	reg_w_riv(gspca_dev, req, idx, val);
3458c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x01, 0x0001, 1);
3468c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_FRAM, "before wait 0x%04x\n",
3478c2ecf20Sopenharmony_ci		  gspca_dev->usb_buf[0]);
3488c2ecf20Sopenharmony_ci	reg_w_riv(gspca_dev, req, idx, val);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	msleep(200);
3518c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x01, 0x0001, 1);
3528c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_FRAM, "after wait 0x%04x\n",
3538c2ecf20Sopenharmony_ci		  gspca_dev->usb_buf[0]);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void spca504_read_info(struct gspca_dev *gspca_dev)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	int i;
3598c2ecf20Sopenharmony_ci	u8 info[6];
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (gspca_debug < D_STREAM)
3628c2ecf20Sopenharmony_ci		return;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
3658c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0, i, 1);
3668c2ecf20Sopenharmony_ci		info[i] = gspca_dev->usb_buf[0];
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM,
3698c2ecf20Sopenharmony_ci		  "Read info: %d %d %d %d %d %d. Should be 1,0,2,2,0,0\n",
3708c2ecf20Sopenharmony_ci		  info[0], info[1], info[2],
3718c2ecf20Sopenharmony_ci		  info[3], info[4], info[5]);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
3758c2ecf20Sopenharmony_ci			u8 req,
3768c2ecf20Sopenharmony_ci			u16 idx, u16 val, u8 endcode, u8 count)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	u16 status;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	reg_w_riv(gspca_dev, req, idx, val);
3818c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x01, 0x0001, 1);
3828c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
3838c2ecf20Sopenharmony_ci		return;
3848c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_FRAM, "Status 0x%02x Need 0x%02x\n",
3858c2ecf20Sopenharmony_ci		  gspca_dev->usb_buf[0], endcode);
3868c2ecf20Sopenharmony_ci	if (!count)
3878c2ecf20Sopenharmony_ci		return;
3888c2ecf20Sopenharmony_ci	count = 200;
3898c2ecf20Sopenharmony_ci	while (--count > 0) {
3908c2ecf20Sopenharmony_ci		msleep(10);
3918c2ecf20Sopenharmony_ci		/* gsmart mini2 write a each wait setting 1 ms is enough */
3928c2ecf20Sopenharmony_ci/*		reg_w_riv(gspca_dev, req, idx, val); */
3938c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x01, 0x0001, 1);
3948c2ecf20Sopenharmony_ci		status = gspca_dev->usb_buf[0];
3958c2ecf20Sopenharmony_ci		if (status == endcode) {
3968c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_FRAM, "status 0x%04x after wait %d\n",
3978c2ecf20Sopenharmony_ci				  status, 200 - count);
3988c2ecf20Sopenharmony_ci				break;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	int count = 10;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	while (--count > 0) {
4088c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x21, 0, 1);
4098c2ecf20Sopenharmony_ci		if ((gspca_dev->usb_buf[0] & 0x01) == 0)
4108c2ecf20Sopenharmony_ci			break;
4118c2ecf20Sopenharmony_ci		msleep(10);
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	int count = 50;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	while (--count > 0) {
4208c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x21, 1, 1);
4218c2ecf20Sopenharmony_ci		if (gspca_dev->usb_buf[0] != 0) {
4228c2ecf20Sopenharmony_ci			reg_w_1(gspca_dev, 0x21, 0, 1, 0);
4238c2ecf20Sopenharmony_ci			reg_r(gspca_dev, 0x21, 1, 1);
4248c2ecf20Sopenharmony_ci			spca504B_PollingDataReady(gspca_dev);
4258c2ecf20Sopenharmony_ci			break;
4268c2ecf20Sopenharmony_ci		}
4278c2ecf20Sopenharmony_ci		msleep(10);
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	u8 *data;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (gspca_debug < D_STREAM)
4368c2ecf20Sopenharmony_ci		return;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	data = gspca_dev->usb_buf;
4398c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x20, 0, 5);
4408c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "FirmWare: %d %d %d %d %d\n",
4418c2ecf20Sopenharmony_ci		  data[0], data[1], data[2], data[3], data[4]);
4428c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x23, 0, 64);
4438c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x23, 1, 64);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4498c2ecf20Sopenharmony_ci	u8 Size;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
4528c2ecf20Sopenharmony_ci	switch (sd->bridge) {
4538c2ecf20Sopenharmony_ci	case BRIDGE_SPCA533:
4548c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x31, 0, 0);
4558c2ecf20Sopenharmony_ci		spca504B_WaitCmdStatus(gspca_dev);
4568c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
4578c2ecf20Sopenharmony_ci		spca50x_GetFirmware(gspca_dev);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		reg_w_1(gspca_dev, 0x24, 0, 8, 2);		/* type */
4608c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x24, 8, 1);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		reg_w_1(gspca_dev, 0x25, 0, 4, Size);
4638c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x25, 4, 1);			/* size */
4648c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		/* Init the cam width height with some values get on init ? */
4678c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);
4688c2ecf20Sopenharmony_ci		spca504B_WaitCmdStatus(gspca_dev);
4698c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci	default:
4728c2ecf20Sopenharmony_ci/* case BRIDGE_SPCA504B: */
4738c2ecf20Sopenharmony_ci/* case BRIDGE_SPCA536: */
4748c2ecf20Sopenharmony_ci		reg_w_1(gspca_dev, 0x25, 0, 4, Size);
4758c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x25, 4, 1);			/* size */
4768c2ecf20Sopenharmony_ci		reg_w_1(gspca_dev, 0x27, 0, 0, 6);
4778c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x27, 0, 1);			/* type */
4788c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504:
4818c2ecf20Sopenharmony_ci		Size += 3;
4828c2ecf20Sopenharmony_ci		if (sd->subtype == AiptekMiniPenCam13) {
4838c2ecf20Sopenharmony_ci			/* spca504a aiptek */
4848c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev,
4858c2ecf20Sopenharmony_ci						0x08, Size, 0,
4868c2ecf20Sopenharmony_ci						0x80 | (Size & 0x0f), 1);
4878c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev,
4888c2ecf20Sopenharmony_ci							1, 3, 0, 0x9f, 0);
4898c2ecf20Sopenharmony_ci		} else {
4908c2ecf20Sopenharmony_ci			spca504_acknowledged_command(gspca_dev, 0x08, Size, 0);
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci		break;
4938c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:
4948c2ecf20Sopenharmony_ci		/* capture mode */
4958c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
4968c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
4978c2ecf20Sopenharmony_ci		break;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void spca504_wait_status(struct gspca_dev *gspca_dev)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	int cnt;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	cnt = 256;
5068c2ecf20Sopenharmony_ci	while (--cnt > 0) {
5078c2ecf20Sopenharmony_ci		/* With this we get the status, when return 0 it's all ok */
5088c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x06, 0x00, 1);
5098c2ecf20Sopenharmony_ci		if (gspca_dev->usb_buf[0] == 0)
5108c2ecf20Sopenharmony_ci			return;
5118c2ecf20Sopenharmony_ci		msleep(10);
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic void spca504B_setQtable(struct gspca_dev *gspca_dev)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	reg_w_1(gspca_dev, 0x26, 0, 0, 3);
5188c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x26, 0, 1);
5198c2ecf20Sopenharmony_ci	spca504B_PollingDataReady(gspca_dev);
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5258c2ecf20Sopenharmony_ci	u16 reg;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
5288c2ecf20Sopenharmony_ci	reg_w_riv(gspca_dev, 0x00, reg, val);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5348c2ecf20Sopenharmony_ci	u16 reg;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
5378c2ecf20Sopenharmony_ci	reg_w_riv(gspca_dev, 0x00, reg, val);
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic void setcolors(struct gspca_dev *gspca_dev, s32 val)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5438c2ecf20Sopenharmony_ci	u16 reg;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
5468c2ecf20Sopenharmony_ci	reg_w_riv(gspca_dev, 0x00, reg, val);
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic void init_ctl_reg(struct gspca_dev *gspca_dev)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5528c2ecf20Sopenharmony_ci	int pollreg = 1;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	switch (sd->bridge) {
5558c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504:
5568c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:
5578c2ecf20Sopenharmony_ci		pollreg = 0;
5588c2ecf20Sopenharmony_ci		fallthrough;
5598c2ecf20Sopenharmony_ci	default:
5608c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA533: */
5618c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504B: */
5628c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x21ad, 0x00);	/* hue */
5638c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x21ac, 0x01);	/* sat/hue */
5648c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x21a3, 0x00);	/* gamma */
5658c2ecf20Sopenharmony_ci		break;
5668c2ecf20Sopenharmony_ci	case BRIDGE_SPCA536:
5678c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x20f5, 0x40);
5688c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x20f4, 0x01);
5698c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x2089, 0x00);
5708c2ecf20Sopenharmony_ci		break;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	if (pollreg)
5738c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/* this function is called at probe time */
5778c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
5788c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5818c2ecf20Sopenharmony_ci	struct cam *cam;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	cam = &gspca_dev->cam;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	sd->bridge = id->driver_info >> 8;
5868c2ecf20Sopenharmony_ci	sd->subtype = id->driver_info;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (sd->subtype == AiptekMiniPenCam13) {
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		/* try to get the firmware as some cam answer 2.0.1.2.2
5918c2ecf20Sopenharmony_ci		 * and should be a spca504b then overwrite that setting */
5928c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x20, 0, 1);
5938c2ecf20Sopenharmony_ci		switch (gspca_dev->usb_buf[0]) {
5948c2ecf20Sopenharmony_ci		case 1:
5958c2ecf20Sopenharmony_ci			break;		/* (right bridge/subtype) */
5968c2ecf20Sopenharmony_ci		case 2:
5978c2ecf20Sopenharmony_ci			sd->bridge = BRIDGE_SPCA504B;
5988c2ecf20Sopenharmony_ci			sd->subtype = 0;
5998c2ecf20Sopenharmony_ci			break;
6008c2ecf20Sopenharmony_ci		default:
6018c2ecf20Sopenharmony_ci			return -ENODEV;
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	switch (sd->bridge) {
6068c2ecf20Sopenharmony_ci	default:
6078c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504B: */
6088c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504: */
6098c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA536: */
6108c2ecf20Sopenharmony_ci		cam->cam_mode = vga_mode;
6118c2ecf20Sopenharmony_ci		cam->nmodes = ARRAY_SIZE(vga_mode);
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	case BRIDGE_SPCA533:
6148c2ecf20Sopenharmony_ci		cam->cam_mode = custom_mode;
6158c2ecf20Sopenharmony_ci		if (sd->subtype == MegaImageVI)		/* 320x240 only */
6168c2ecf20Sopenharmony_ci			cam->nmodes = ARRAY_SIZE(custom_mode) - 1;
6178c2ecf20Sopenharmony_ci		else
6188c2ecf20Sopenharmony_ci			cam->nmodes = ARRAY_SIZE(custom_mode);
6198c2ecf20Sopenharmony_ci		break;
6208c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:
6218c2ecf20Sopenharmony_ci		cam->cam_mode = vga_mode2;
6228c2ecf20Sopenharmony_ci		cam->nmodes = ARRAY_SIZE(vga_mode2);
6238c2ecf20Sopenharmony_ci		break;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	return 0;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
6298c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	switch (sd->bridge) {
6348c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504B:
6358c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x1d, 0x00, 0);
6368c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01);
6378c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00);
6388c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00);
6398c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13);
6408c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00);
6418c2ecf20Sopenharmony_ci		fallthrough;
6428c2ecf20Sopenharmony_ci	case BRIDGE_SPCA533:
6438c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
6448c2ecf20Sopenharmony_ci		spca50x_GetFirmware(gspca_dev);
6458c2ecf20Sopenharmony_ci		break;
6468c2ecf20Sopenharmony_ci	case BRIDGE_SPCA536:
6478c2ecf20Sopenharmony_ci		spca50x_GetFirmware(gspca_dev);
6488c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x00, 0x5002, 1);
6498c2ecf20Sopenharmony_ci		reg_w_1(gspca_dev, 0x24, 0, 0, 0);
6508c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x24, 0, 1);
6518c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
6528c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x34, 0, 0);
6538c2ecf20Sopenharmony_ci		spca504B_WaitCmdStatus(gspca_dev);
6548c2ecf20Sopenharmony_ci		break;
6558c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:	/* pccam600 */
6568c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504 (PC-CAM 600)\n");
6578c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000);
6588c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001);	/* reset */
6598c2ecf20Sopenharmony_ci		spca504_wait_status(gspca_dev);
6608c2ecf20Sopenharmony_ci		if (sd->subtype == LogitechClickSmart420)
6618c2ecf20Sopenharmony_ci			write_vector(gspca_dev,
6628c2ecf20Sopenharmony_ci				spca504A_clicksmart420_open_data,
6638c2ecf20Sopenharmony_ci				ARRAY_SIZE(spca504A_clicksmart420_open_data));
6648c2ecf20Sopenharmony_ci		else
6658c2ecf20Sopenharmony_ci			write_vector(gspca_dev, spca504_pccam600_open_data,
6668c2ecf20Sopenharmony_ci				ARRAY_SIZE(spca504_pccam600_open_data));
6678c2ecf20Sopenharmony_ci		setup_qtable(gspca_dev, qtable_creative_pccam);
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci	default:
6708c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504: */
6718c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504\n");
6728c2ecf20Sopenharmony_ci		if (sd->subtype == AiptekMiniPenCam13) {
6738c2ecf20Sopenharmony_ci			spca504_read_info(gspca_dev);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci			/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
6768c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
6778c2ecf20Sopenharmony_ci							8, 3, 0x9e, 1);
6788c2ecf20Sopenharmony_ci			/* Twice sequential need status 0xff->0x9e->0x9d */
6798c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
6808c2ecf20Sopenharmony_ci							8, 3, 0x9e, 0);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
6838c2ecf20Sopenharmony_ci							0, 0, 0x9d, 1);
6848c2ecf20Sopenharmony_ci			/******************************/
6858c2ecf20Sopenharmony_ci			/* spca504a aiptek */
6868c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x08,
6878c2ecf20Sopenharmony_ci							6, 0, 0x86, 1);
6888c2ecf20Sopenharmony_ci/*			reg_write (dev, 0, 0x2000, 0); */
6898c2ecf20Sopenharmony_ci/*			reg_write (dev, 0, 0x2883, 1); */
6908c2ecf20Sopenharmony_ci/*			spca504A_acknowledged_command (gspca_dev, 0x08,
6918c2ecf20Sopenharmony_ci							6, 0, 0x86, 1); */
6928c2ecf20Sopenharmony_ci/*			spca504A_acknowledged_command (gspca_dev, 0x24,
6938c2ecf20Sopenharmony_ci							0, 0, 0x9D, 1); */
6948c2ecf20Sopenharmony_ci			reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
6958c2ecf20Sopenharmony_ci							/* L92 sno1t.txt */
6968c2ecf20Sopenharmony_ci			reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
6978c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x01,
6988c2ecf20Sopenharmony_ci							0x0f, 0, 0xff, 0);
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci		/* setup qtable */
7018c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x2000, 0);
7028c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0, 0x2883, 1);
7038c2ecf20Sopenharmony_ci		setup_qtable(gspca_dev, qtable_spca504_default);
7048c2ecf20Sopenharmony_ci		break;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7128c2ecf20Sopenharmony_ci	int enable;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	/* create the JPEG header */
7158c2ecf20Sopenharmony_ci	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
7168c2ecf20Sopenharmony_ci			gspca_dev->pixfmt.width,
7178c2ecf20Sopenharmony_ci			0x22);		/* JPEG 411 */
7188c2ecf20Sopenharmony_ci	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (sd->bridge == BRIDGE_SPCA504B)
7218c2ecf20Sopenharmony_ci		spca504B_setQtable(gspca_dev);
7228c2ecf20Sopenharmony_ci	spca504B_SetSizeType(gspca_dev);
7238c2ecf20Sopenharmony_ci	switch (sd->bridge) {
7248c2ecf20Sopenharmony_ci	default:
7258c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504B: */
7268c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA533: */
7278c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA536: */
7288c2ecf20Sopenharmony_ci		switch (sd->subtype) {
7298c2ecf20Sopenharmony_ci		case MegapixV4:
7308c2ecf20Sopenharmony_ci		case LogitechClickSmart820:
7318c2ecf20Sopenharmony_ci		case MegaImageVI:
7328c2ecf20Sopenharmony_ci			reg_w_riv(gspca_dev, 0xf0, 0, 0);
7338c2ecf20Sopenharmony_ci			spca504B_WaitCmdStatus(gspca_dev);
7348c2ecf20Sopenharmony_ci			reg_w_riv(gspca_dev, 0xf0, 4, 0);
7358c2ecf20Sopenharmony_ci			spca504B_WaitCmdStatus(gspca_dev);
7368c2ecf20Sopenharmony_ci			break;
7378c2ecf20Sopenharmony_ci		default:
7388c2ecf20Sopenharmony_ci			reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);
7398c2ecf20Sopenharmony_ci			spca504B_WaitCmdStatus(gspca_dev);
7408c2ecf20Sopenharmony_ci			spca504B_PollingDataReady(gspca_dev);
7418c2ecf20Sopenharmony_ci			break;
7428c2ecf20Sopenharmony_ci		}
7438c2ecf20Sopenharmony_ci		break;
7448c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504:
7458c2ecf20Sopenharmony_ci		if (sd->subtype == AiptekMiniPenCam13) {
7468c2ecf20Sopenharmony_ci			spca504_read_info(gspca_dev);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci			/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
7498c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
7508c2ecf20Sopenharmony_ci							8, 3, 0x9e, 1);
7518c2ecf20Sopenharmony_ci			/* Twice sequential need status 0xff->0x9e->0x9d */
7528c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
7538c2ecf20Sopenharmony_ci							8, 3, 0x9e, 0);
7548c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
7558c2ecf20Sopenharmony_ci							0, 0, 0x9d, 1);
7568c2ecf20Sopenharmony_ci		} else {
7578c2ecf20Sopenharmony_ci			spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
7588c2ecf20Sopenharmony_ci			spca504_read_info(gspca_dev);
7598c2ecf20Sopenharmony_ci			spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
7608c2ecf20Sopenharmony_ci			spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
7618c2ecf20Sopenharmony_ci		}
7628c2ecf20Sopenharmony_ci		spca504B_SetSizeType(gspca_dev);
7638c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
7648c2ecf20Sopenharmony_ci							/* L92 sno1t.txt */
7658c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:
7688c2ecf20Sopenharmony_ci		if (sd->subtype == LogitechClickSmart420) {
7698c2ecf20Sopenharmony_ci			write_vector(gspca_dev,
7708c2ecf20Sopenharmony_ci				spca504A_clicksmart420_init_data,
7718c2ecf20Sopenharmony_ci				ARRAY_SIZE(spca504A_clicksmart420_init_data));
7728c2ecf20Sopenharmony_ci		} else {
7738c2ecf20Sopenharmony_ci			write_vector(gspca_dev, spca504_pccam600_init_data,
7748c2ecf20Sopenharmony_ci				ARRAY_SIZE(spca504_pccam600_init_data));
7758c2ecf20Sopenharmony_ci		}
7768c2ecf20Sopenharmony_ci		enable = (sd->autogain ? 0x04 : 0x01);
7778c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x0c, 0x0000, enable);
7788c2ecf20Sopenharmony_ci							/* auto exposure */
7798c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0xb0, 0x0000, enable);
7808c2ecf20Sopenharmony_ci							/* auto whiteness */
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		/* set default exposure compensation and whiteness balance */
7838c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x30, 0x0001, 800);	/* ~ 20 fps */
7848c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x30, 0x0002, 1600);
7858c2ecf20Sopenharmony_ci		spca504B_SetSizeType(gspca_dev);
7868c2ecf20Sopenharmony_ci		break;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci	init_ctl_reg(gspca_dev);
7898c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	switch (sd->bridge) {
7978c2ecf20Sopenharmony_ci	default:
7988c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA533: */
7998c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA536: */
8008c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504B: */
8018c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x31, 0, 0);
8028c2ecf20Sopenharmony_ci		spca504B_WaitCmdStatus(gspca_dev);
8038c2ecf20Sopenharmony_ci		spca504B_PollingDataReady(gspca_dev);
8048c2ecf20Sopenharmony_ci		break;
8058c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504:
8068c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:
8078c2ecf20Sopenharmony_ci		reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		if (sd->subtype == AiptekMiniPenCam13) {
8108c2ecf20Sopenharmony_ci			/* spca504a aiptek */
8118c2ecf20Sopenharmony_ci/*			spca504A_acknowledged_command(gspca_dev, 0x08,
8128c2ecf20Sopenharmony_ci							 6, 0, 0x86, 1); */
8138c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x24,
8148c2ecf20Sopenharmony_ci							0x00, 0x00, 0x9d, 1);
8158c2ecf20Sopenharmony_ci			spca504A_acknowledged_command(gspca_dev, 0x01,
8168c2ecf20Sopenharmony_ci							0x0f, 0x00, 0xff, 1);
8178c2ecf20Sopenharmony_ci		} else {
8188c2ecf20Sopenharmony_ci			spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
8198c2ecf20Sopenharmony_ci			reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000);
8208c2ecf20Sopenharmony_ci		}
8218c2ecf20Sopenharmony_ci		break;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
8268c2ecf20Sopenharmony_ci			u8 *data,			/* isoc packet */
8278c2ecf20Sopenharmony_ci			int len)			/* iso packet length */
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8308c2ecf20Sopenharmony_ci	int i, sof = 0;
8318c2ecf20Sopenharmony_ci	static u8 ffd9[] = {0xff, 0xd9};
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci/* frames are jpeg 4.1.1 without 0xff escape */
8348c2ecf20Sopenharmony_ci	switch (sd->bridge) {
8358c2ecf20Sopenharmony_ci	case BRIDGE_SPCA533:
8368c2ecf20Sopenharmony_ci		if (data[0] == 0xff) {
8378c2ecf20Sopenharmony_ci			if (data[1] != 0x01) {	/* drop packet */
8388c2ecf20Sopenharmony_ci/*				gspca_dev->last_packet_type = DISCARD_PACKET; */
8398c2ecf20Sopenharmony_ci				return;
8408c2ecf20Sopenharmony_ci			}
8418c2ecf20Sopenharmony_ci			sof = 1;
8428c2ecf20Sopenharmony_ci			data += SPCA533_OFFSET_DATA;
8438c2ecf20Sopenharmony_ci			len -= SPCA533_OFFSET_DATA;
8448c2ecf20Sopenharmony_ci		} else {
8458c2ecf20Sopenharmony_ci			data += 1;
8468c2ecf20Sopenharmony_ci			len -= 1;
8478c2ecf20Sopenharmony_ci		}
8488c2ecf20Sopenharmony_ci		break;
8498c2ecf20Sopenharmony_ci	case BRIDGE_SPCA536:
8508c2ecf20Sopenharmony_ci		if (data[0] == 0xff) {
8518c2ecf20Sopenharmony_ci			sof = 1;
8528c2ecf20Sopenharmony_ci			data += SPCA536_OFFSET_DATA;
8538c2ecf20Sopenharmony_ci			len -= SPCA536_OFFSET_DATA;
8548c2ecf20Sopenharmony_ci		} else {
8558c2ecf20Sopenharmony_ci			data += 2;
8568c2ecf20Sopenharmony_ci			len -= 2;
8578c2ecf20Sopenharmony_ci		}
8588c2ecf20Sopenharmony_ci		break;
8598c2ecf20Sopenharmony_ci	default:
8608c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504: */
8618c2ecf20Sopenharmony_ci/*	case BRIDGE_SPCA504B: */
8628c2ecf20Sopenharmony_ci		switch (data[0]) {
8638c2ecf20Sopenharmony_ci		case 0xfe:			/* start of frame */
8648c2ecf20Sopenharmony_ci			sof = 1;
8658c2ecf20Sopenharmony_ci			data += SPCA50X_OFFSET_DATA;
8668c2ecf20Sopenharmony_ci			len -= SPCA50X_OFFSET_DATA;
8678c2ecf20Sopenharmony_ci			break;
8688c2ecf20Sopenharmony_ci		case 0xff:			/* drop packet */
8698c2ecf20Sopenharmony_ci/*			gspca_dev->last_packet_type = DISCARD_PACKET; */
8708c2ecf20Sopenharmony_ci			return;
8718c2ecf20Sopenharmony_ci		default:
8728c2ecf20Sopenharmony_ci			data += 1;
8738c2ecf20Sopenharmony_ci			len -= 1;
8748c2ecf20Sopenharmony_ci			break;
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci		break;
8778c2ecf20Sopenharmony_ci	case BRIDGE_SPCA504C:
8788c2ecf20Sopenharmony_ci		switch (data[0]) {
8798c2ecf20Sopenharmony_ci		case 0xfe:			/* start of frame */
8808c2ecf20Sopenharmony_ci			sof = 1;
8818c2ecf20Sopenharmony_ci			data += SPCA504_PCCAM600_OFFSET_DATA;
8828c2ecf20Sopenharmony_ci			len -= SPCA504_PCCAM600_OFFSET_DATA;
8838c2ecf20Sopenharmony_ci			break;
8848c2ecf20Sopenharmony_ci		case 0xff:			/* drop packet */
8858c2ecf20Sopenharmony_ci/*			gspca_dev->last_packet_type = DISCARD_PACKET; */
8868c2ecf20Sopenharmony_ci			return;
8878c2ecf20Sopenharmony_ci		default:
8888c2ecf20Sopenharmony_ci			data += 1;
8898c2ecf20Sopenharmony_ci			len -= 1;
8908c2ecf20Sopenharmony_ci			break;
8918c2ecf20Sopenharmony_ci		}
8928c2ecf20Sopenharmony_ci		break;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci	if (sof) {		/* start of frame */
8958c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET,
8968c2ecf20Sopenharmony_ci				ffd9, 2);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		/* put the JPEG header in the new frame */
8998c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET,
9008c2ecf20Sopenharmony_ci			sd->jpeg_hdr, JPEG_HDR_SZ);
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* add 0x00 after 0xff */
9048c2ecf20Sopenharmony_ci	i = 0;
9058c2ecf20Sopenharmony_ci	do {
9068c2ecf20Sopenharmony_ci		if (data[i] == 0xff) {
9078c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, INTER_PACKET,
9088c2ecf20Sopenharmony_ci					data, i + 1);
9098c2ecf20Sopenharmony_ci			len -= i;
9108c2ecf20Sopenharmony_ci			data += i;
9118c2ecf20Sopenharmony_ci			*data = 0x00;
9128c2ecf20Sopenharmony_ci			i = 0;
9138c2ecf20Sopenharmony_ci		}
9148c2ecf20Sopenharmony_ci		i++;
9158c2ecf20Sopenharmony_ci	} while (i < len);
9168c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
9228c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
9238c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	gspca_dev->usb_err = 0;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming)
9288c2ecf20Sopenharmony_ci		return 0;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	switch (ctrl->id) {
9318c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
9328c2ecf20Sopenharmony_ci		setbrightness(gspca_dev, ctrl->val);
9338c2ecf20Sopenharmony_ci		break;
9348c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
9358c2ecf20Sopenharmony_ci		setcontrast(gspca_dev, ctrl->val);
9368c2ecf20Sopenharmony_ci		break;
9378c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
9388c2ecf20Sopenharmony_ci		setcolors(gspca_dev, ctrl->val);
9398c2ecf20Sopenharmony_ci		break;
9408c2ecf20Sopenharmony_ci	case V4L2_CID_AUTOGAIN:
9418c2ecf20Sopenharmony_ci		sd->autogain = ctrl->val;
9428c2ecf20Sopenharmony_ci		break;
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
9488c2ecf20Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
9498c2ecf20Sopenharmony_ci};
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
9568c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 4);
9578c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9588c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
9598c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9608c2ecf20Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 255, 1, 0x20);
9618c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9628c2ecf20Sopenharmony_ci			V4L2_CID_SATURATION, 0, 255, 1, 0x1a);
9638c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
9648c2ecf20Sopenharmony_ci			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (hdl->error) {
9678c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
9688c2ecf20Sopenharmony_ci		return hdl->error;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci	return 0;
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci/* sub-driver description */
9748c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
9758c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
9768c2ecf20Sopenharmony_ci	.config = sd_config,
9778c2ecf20Sopenharmony_ci	.init = sd_init,
9788c2ecf20Sopenharmony_ci	.init_controls = sd_init_controls,
9798c2ecf20Sopenharmony_ci	.start = sd_start,
9808c2ecf20Sopenharmony_ci	.stopN = sd_stopN,
9818c2ecf20Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
9828c2ecf20Sopenharmony_ci};
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci/* -- module initialisation -- */
9858c2ecf20Sopenharmony_ci#define BS(bridge, subtype) \
9868c2ecf20Sopenharmony_ci	.driver_info = (BRIDGE_ ## bridge << 8) \
9878c2ecf20Sopenharmony_ci			| (subtype)
9888c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
9898c2ecf20Sopenharmony_ci	{USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)},
9908c2ecf20Sopenharmony_ci	{USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)},
9918c2ecf20Sopenharmony_ci	{USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)},
9928c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)},
9938c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)},
9948c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)},
9958c2ecf20Sopenharmony_ci	{USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)},
9968c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)},
9978c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)},
9988c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)},
9998c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)},
10008c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)},
10018c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)},
10028c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)},
10038c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)},
10048c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},
10058c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
10068c2ecf20Sopenharmony_ci	{USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
10078c2ecf20Sopenharmony_ci	{USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},
10088c2ecf20Sopenharmony_ci	{USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
10098c2ecf20Sopenharmony_ci	{USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
10108c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
10118c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)},
10128c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)},
10138c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)},
10148c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)},
10158c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)},
10168c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)},
10178c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)},
10188c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)},
10198c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)},
10208c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)},
10218c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)},
10228c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)},
10238c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)},
10248c2ecf20Sopenharmony_ci	{USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)},
10258c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)},
10268c2ecf20Sopenharmony_ci	{USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)},
10278c2ecf20Sopenharmony_ci	{USB_DEVICE(0x06d6, 0x0041), BS(SPCA504B, 0)},
10288c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)},
10298c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)},
10308c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)},
10318c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)},
10328c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)},
10338c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)},
10348c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)},
10358c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)},
10368c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)},
10378c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)},
10388c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)},
10398c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)},
10408c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)},
10418c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)},
10428c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)},
10438c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)},
10448c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)},
10458c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)},
10468c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)},
10478c2ecf20Sopenharmony_ci	{USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)},
10488c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)},
10498c2ecf20Sopenharmony_ci	{}
10508c2ecf20Sopenharmony_ci};
10518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci/* -- device connect -- */
10548c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
10558c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
10588c2ecf20Sopenharmony_ci				THIS_MODULE);
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
10628c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
10638c2ecf20Sopenharmony_ci	.id_table = device_table,
10648c2ecf20Sopenharmony_ci	.probe = sd_probe,
10658c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
10668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
10678c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
10688c2ecf20Sopenharmony_ci	.resume = gspca_resume,
10698c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
10708c2ecf20Sopenharmony_ci#endif
10718c2ecf20Sopenharmony_ci};
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
1074