18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *		Pixart PAC7311 library
48c2ecf20Sopenharmony_ci *		Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/* Some documentation about various registers as determined by trial and error.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Register page 1:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Address	Description
148c2ecf20Sopenharmony_ci * 0x08		Unknown compressor related, must always be 8 except when not
158c2ecf20Sopenharmony_ci *		in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
168c2ecf20Sopenharmony_ci * 0x1b		Auto white balance related, bit 0 is AWB enable (inverted)
178c2ecf20Sopenharmony_ci *		bits 345 seem to toggle per color gains on/off (inverted)
188c2ecf20Sopenharmony_ci * 0x78		Global control, bit 6 controls the LED (inverted)
198c2ecf20Sopenharmony_ci * 0x80		Compression balance, interesting settings:
208c2ecf20Sopenharmony_ci *		0x01 Use this to allow the camera to switch to higher compr.
218c2ecf20Sopenharmony_ci *		     on the fly. Needed to stay within bandwidth @ 640x480@30
228c2ecf20Sopenharmony_ci *		0x1c From usb captures under Windows for 640x480
238c2ecf20Sopenharmony_ci *		0x2a Values >= this switch the camera to a lower compression,
248c2ecf20Sopenharmony_ci *		     using the same table for both luminance and chrominance.
258c2ecf20Sopenharmony_ci *		     This gives a sharper picture. Usable only at 640x480@ <
268c2ecf20Sopenharmony_ci *		     15 fps or 320x240 / 160x120. Note currently the driver
278c2ecf20Sopenharmony_ci *		     does not use this as the quality gain is small and the
288c2ecf20Sopenharmony_ci *		     generated JPG-s are only understood by v4l-utils >= 0.8.9
298c2ecf20Sopenharmony_ci *		0x3f From usb captures under Windows for 320x240
308c2ecf20Sopenharmony_ci *		0x69 From usb captures under Windows for 160x120
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Register page 4:
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Address	Description
358c2ecf20Sopenharmony_ci * 0x02		Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
368c2ecf20Sopenharmony_ci *		the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
378c2ecf20Sopenharmony_ci * 0x0f		Master gain 1-245, low value = high gain
388c2ecf20Sopenharmony_ci * 0x10		Another gain 0-15, limited influence (1-2x gain I guess)
398c2ecf20Sopenharmony_ci * 0x21		Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
408c2ecf20Sopenharmony_ci *		Note setting vflip disabled leads to a much lower image quality,
418c2ecf20Sopenharmony_ci *		so we always vflip, and tell userspace to flip it back
428c2ecf20Sopenharmony_ci * 0x27		Seems to toggle various gains on / off, Setting bit 7 seems to
438c2ecf20Sopenharmony_ci *		completely disable the analog amplification block. Set to 0x68
448c2ecf20Sopenharmony_ci *		for max gain, 0x14 for minimal gain.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define MODULE_NAME "pac7311"
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <linux/input.h>
528c2ecf20Sopenharmony_ci#include "gspca.h"
538c2ecf20Sopenharmony_ci/* Include pac common sof detection functions */
548c2ecf20Sopenharmony_ci#include "pac_common.h"
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define PAC7311_GAIN_DEFAULT     122
578c2ecf20Sopenharmony_ci#define PAC7311_EXPOSURE_DEFAULT   3 /* 20 fps, avoid using high compr. */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pixart PAC7311");
618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct sd {
648c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev;		/* !! must be the first item */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	struct v4l2_ctrl *contrast;
678c2ecf20Sopenharmony_ci	struct v4l2_ctrl *hflip;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	u8 sof_read;
708c2ecf20Sopenharmony_ci	u8 autogain_ignore_frames;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	atomic_t avg_lum;
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = {
768c2ecf20Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
778c2ecf20Sopenharmony_ci		.bytesperline = 160,
788c2ecf20Sopenharmony_ci		.sizeimage = 160 * 120 * 3 / 8 + 590,
798c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
808c2ecf20Sopenharmony_ci		.priv = 2},
818c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
828c2ecf20Sopenharmony_ci		.bytesperline = 320,
838c2ecf20Sopenharmony_ci		.sizeimage = 320 * 240 * 3 / 8 + 590,
848c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
858c2ecf20Sopenharmony_ci		.priv = 1},
868c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
878c2ecf20Sopenharmony_ci		.bytesperline = 640,
888c2ecf20Sopenharmony_ci		.sizeimage = 640 * 480 * 3 / 8 + 590,
898c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
908c2ecf20Sopenharmony_ci		.priv = 0},
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define LOAD_PAGE4		254
948c2ecf20Sopenharmony_ci#define END_OF_SEQUENCE		0
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic const __u8 init_7311[] = {
978c2ecf20Sopenharmony_ci	0xff, 0x01,
988c2ecf20Sopenharmony_ci	0x78, 0x40,	/* Bit_0=start stream, Bit_6=LED */
998c2ecf20Sopenharmony_ci	0x78, 0x40,	/* Bit_0=start stream, Bit_6=LED */
1008c2ecf20Sopenharmony_ci	0x78, 0x44,	/* Bit_0=start stream, Bit_6=LED */
1018c2ecf20Sopenharmony_ci	0xff, 0x04,
1028c2ecf20Sopenharmony_ci	0x27, 0x80,
1038c2ecf20Sopenharmony_ci	0x28, 0xca,
1048c2ecf20Sopenharmony_ci	0x29, 0x53,
1058c2ecf20Sopenharmony_ci	0x2a, 0x0e,
1068c2ecf20Sopenharmony_ci	0xff, 0x01,
1078c2ecf20Sopenharmony_ci	0x3e, 0x20,
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic const __u8 start_7311[] = {
1118c2ecf20Sopenharmony_ci/*	index, len, [value]* */
1128c2ecf20Sopenharmony_ci	0xff, 1,	0x01,		/* page 1 */
1138c2ecf20Sopenharmony_ci	0x02, 43,	0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,
1148c2ecf20Sopenharmony_ci			0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,
1158c2ecf20Sopenharmony_ci			0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,
1168c2ecf20Sopenharmony_ci			0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,
1178c2ecf20Sopenharmony_ci			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1188c2ecf20Sopenharmony_ci			0x00, 0x00, 0x00,
1198c2ecf20Sopenharmony_ci	0x3e, 42,	0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,
1208c2ecf20Sopenharmony_ci			0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,
1218c2ecf20Sopenharmony_ci			0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,
1228c2ecf20Sopenharmony_ci			0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,
1238c2ecf20Sopenharmony_ci			0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,
1248c2ecf20Sopenharmony_ci			0xd0, 0xff,
1258c2ecf20Sopenharmony_ci	0x78, 6,	0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,
1268c2ecf20Sopenharmony_ci	0x7f, 18,	0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,
1278c2ecf20Sopenharmony_ci			0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,
1288c2ecf20Sopenharmony_ci			0x18, 0x20,
1298c2ecf20Sopenharmony_ci	0x96, 3,	0x01, 0x08, 0x04,
1308c2ecf20Sopenharmony_ci	0xa0, 4,	0x44, 0x44, 0x44, 0x04,
1318c2ecf20Sopenharmony_ci	0xf0, 13,	0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
1328c2ecf20Sopenharmony_ci			0x3f, 0x00, 0x0a, 0x01, 0x00,
1338c2ecf20Sopenharmony_ci	0xff, 1,	0x04,		/* page 4 */
1348c2ecf20Sopenharmony_ci	0, LOAD_PAGE4,			/* load the page 4 */
1358c2ecf20Sopenharmony_ci	0x11, 1,	0x01,
1368c2ecf20Sopenharmony_ci	0, END_OF_SEQUENCE		/* end of sequence */
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define SKIP		0xaa
1408c2ecf20Sopenharmony_ci/* page 4 - the value SKIP says skip the index - see reg_w_page() */
1418c2ecf20Sopenharmony_cistatic const __u8 page4_7311[] = {
1428c2ecf20Sopenharmony_ci	SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
1438c2ecf20Sopenharmony_ci	0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
1448c2ecf20Sopenharmony_ci	0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
1458c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
1468c2ecf20Sopenharmony_ci	SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
1478c2ecf20Sopenharmony_ci	0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
1488c2ecf20Sopenharmony_ci	0x23, 0x28, 0x04, 0x11, 0x00, 0x00
1498c2ecf20Sopenharmony_ci};
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void reg_w_buf(struct gspca_dev *gspca_dev,
1528c2ecf20Sopenharmony_ci		  __u8 index,
1538c2ecf20Sopenharmony_ci		  const u8 *buffer, int len)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
1588c2ecf20Sopenharmony_ci		return;
1598c2ecf20Sopenharmony_ci	memcpy(gspca_dev->usb_buf, buffer, len);
1608c2ecf20Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
1618c2ecf20Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
1628c2ecf20Sopenharmony_ci			0,		/* request */
1638c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1648c2ecf20Sopenharmony_ci			0,		/* value */
1658c2ecf20Sopenharmony_ci			index, gspca_dev->usb_buf, len,
1668c2ecf20Sopenharmony_ci			500);
1678c2ecf20Sopenharmony_ci	if (ret < 0) {
1688c2ecf20Sopenharmony_ci		pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
1698c2ecf20Sopenharmony_ci		       index, ret);
1708c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev,
1768c2ecf20Sopenharmony_ci		  __u8 index,
1778c2ecf20Sopenharmony_ci		  __u8 value)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int ret;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
1828c2ecf20Sopenharmony_ci		return;
1838c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = value;
1848c2ecf20Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev,
1858c2ecf20Sopenharmony_ci			usb_sndctrlpipe(gspca_dev->dev, 0),
1868c2ecf20Sopenharmony_ci			0,			/* request */
1878c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1888c2ecf20Sopenharmony_ci			0, index, gspca_dev->usb_buf, 1,
1898c2ecf20Sopenharmony_ci			500);
1908c2ecf20Sopenharmony_ci	if (ret < 0) {
1918c2ecf20Sopenharmony_ci		pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
1928c2ecf20Sopenharmony_ci		       index, value, ret);
1938c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void reg_w_seq(struct gspca_dev *gspca_dev,
1988c2ecf20Sopenharmony_ci		const __u8 *seq, int len)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	while (--len >= 0) {
2018c2ecf20Sopenharmony_ci		reg_w(gspca_dev, seq[0], seq[1]);
2028c2ecf20Sopenharmony_ci		seq += 2;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* load the beginning of a page */
2078c2ecf20Sopenharmony_cistatic void reg_w_page(struct gspca_dev *gspca_dev,
2088c2ecf20Sopenharmony_ci			const __u8 *page, int len)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int index;
2118c2ecf20Sopenharmony_ci	int ret = 0;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
2148c2ecf20Sopenharmony_ci		return;
2158c2ecf20Sopenharmony_ci	for (index = 0; index < len; index++) {
2168c2ecf20Sopenharmony_ci		if (page[index] == SKIP)		/* skip this index */
2178c2ecf20Sopenharmony_ci			continue;
2188c2ecf20Sopenharmony_ci		gspca_dev->usb_buf[0] = page[index];
2198c2ecf20Sopenharmony_ci		ret = usb_control_msg(gspca_dev->dev,
2208c2ecf20Sopenharmony_ci				usb_sndctrlpipe(gspca_dev->dev, 0),
2218c2ecf20Sopenharmony_ci				0,			/* request */
2228c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2238c2ecf20Sopenharmony_ci				0, index, gspca_dev->usb_buf, 1,
2248c2ecf20Sopenharmony_ci				500);
2258c2ecf20Sopenharmony_ci		if (ret < 0) {
2268c2ecf20Sopenharmony_ci			pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
2278c2ecf20Sopenharmony_ci			       index, page[index], ret);
2288c2ecf20Sopenharmony_ci			gspca_dev->usb_err = ret;
2298c2ecf20Sopenharmony_ci			break;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci/* output a variable sequence */
2358c2ecf20Sopenharmony_cistatic void reg_w_var(struct gspca_dev *gspca_dev,
2368c2ecf20Sopenharmony_ci			const __u8 *seq,
2378c2ecf20Sopenharmony_ci			const __u8 *page4, unsigned int page4_len)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int index, len;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	for (;;) {
2428c2ecf20Sopenharmony_ci		index = *seq++;
2438c2ecf20Sopenharmony_ci		len = *seq++;
2448c2ecf20Sopenharmony_ci		switch (len) {
2458c2ecf20Sopenharmony_ci		case END_OF_SEQUENCE:
2468c2ecf20Sopenharmony_ci			return;
2478c2ecf20Sopenharmony_ci		case LOAD_PAGE4:
2488c2ecf20Sopenharmony_ci			reg_w_page(gspca_dev, page4, page4_len);
2498c2ecf20Sopenharmony_ci			break;
2508c2ecf20Sopenharmony_ci		default:
2518c2ecf20Sopenharmony_ci			if (len > USB_BUF_SZ) {
2528c2ecf20Sopenharmony_ci				gspca_err(gspca_dev, "Incorrect variable sequence\n");
2538c2ecf20Sopenharmony_ci				return;
2548c2ecf20Sopenharmony_ci			}
2558c2ecf20Sopenharmony_ci			while (len > 0) {
2568c2ecf20Sopenharmony_ci				if (len < 8) {
2578c2ecf20Sopenharmony_ci					reg_w_buf(gspca_dev,
2588c2ecf20Sopenharmony_ci						index, seq, len);
2598c2ecf20Sopenharmony_ci					seq += len;
2608c2ecf20Sopenharmony_ci					break;
2618c2ecf20Sopenharmony_ci				}
2628c2ecf20Sopenharmony_ci				reg_w_buf(gspca_dev, index, seq, 8);
2638c2ecf20Sopenharmony_ci				seq += 8;
2648c2ecf20Sopenharmony_ci				index += 8;
2658c2ecf20Sopenharmony_ci				len -= 8;
2668c2ecf20Sopenharmony_ci			}
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	/* not reached */
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/* this function is called at probe time for pac7311 */
2738c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
2748c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct cam *cam = &gspca_dev->cam;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	cam->cam_mode = vga_mode;
2798c2ecf20Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(vga_mode);
2808c2ecf20Sopenharmony_ci	cam->input_flags = V4L2_IN_ST_VFLIP;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x04);
2888c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x10, val);
2898c2ecf20Sopenharmony_ci	/* load registers to sensor (Bit 0, auto clear) */
2908c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x11, 0x01);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x04);			/* page 4 */
2968c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0e, 0x00);
2978c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* load registers to sensor (Bit 0, auto clear) */
3008c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x11, 0x01);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 val)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x04);			/* page 4 */
3068c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x02, val);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* load registers to sensor (Bit 0, auto clear) */
3098c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x11, 0x01);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/*
3128c2ecf20Sopenharmony_ci	 * Page 1 register 8 must always be 0x08 except when not in
3138c2ecf20Sopenharmony_ci	 *  640x480 mode and page 4 reg 2 <= 3 then it must be 9
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x01);
3168c2ecf20Sopenharmony_ci	if (gspca_dev->pixfmt.width != 640 && val <= 3)
3178c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x08, 0x09);
3188c2ecf20Sopenharmony_ci	else
3198c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x08, 0x08);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/*
3228c2ecf20Sopenharmony_ci	 * Page1 register 80 sets the compression balance, normally we
3238c2ecf20Sopenharmony_ci	 * want / use 0x1c, but for 640x480@30fps we must allow the
3248c2ecf20Sopenharmony_ci	 * camera to use higher compression or we may run out of
3258c2ecf20Sopenharmony_ci	 * bandwidth.
3268c2ecf20Sopenharmony_ci	 */
3278c2ecf20Sopenharmony_ci	if (gspca_dev->pixfmt.width == 640 && val == 2)
3288c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x80, 0x01);
3298c2ecf20Sopenharmony_ci	else
3308c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x80, 0x1c);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* load registers to sensor (Bit 0, auto clear) */
3338c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x11, 0x01);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	__u8 data;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x04);			/* page 4 */
3418c2ecf20Sopenharmony_ci	data = (hflip ? 0x04 : 0x00) |
3428c2ecf20Sopenharmony_ci	       (vflip ? 0x08 : 0x00);
3438c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x21, data);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* load registers to sensor (Bit 0, auto clear) */
3468c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x11, 0x01);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/* this function is called at probe and resume time for pac7311 */
3508c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
3538c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
3598c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
3608c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	gspca_dev->usb_err = 0;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
3658c2ecf20Sopenharmony_ci		/* when switching to autogain set defaults to make sure
3668c2ecf20Sopenharmony_ci		   we are on a valid point of the autogain gain /
3678c2ecf20Sopenharmony_ci		   exposure knee graph, and give this change time to
3688c2ecf20Sopenharmony_ci		   take effect before doing autogain. */
3698c2ecf20Sopenharmony_ci		gspca_dev->exposure->val    = PAC7311_EXPOSURE_DEFAULT;
3708c2ecf20Sopenharmony_ci		gspca_dev->gain->val        = PAC7311_GAIN_DEFAULT;
3718c2ecf20Sopenharmony_ci		sd->autogain_ignore_frames  = PAC_AUTOGAIN_IGNORE_FRAMES;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming)
3758c2ecf20Sopenharmony_ci		return 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	switch (ctrl->id) {
3788c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
3798c2ecf20Sopenharmony_ci		setcontrast(gspca_dev, ctrl->val);
3808c2ecf20Sopenharmony_ci		break;
3818c2ecf20Sopenharmony_ci	case V4L2_CID_AUTOGAIN:
3828c2ecf20Sopenharmony_ci		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
3838c2ecf20Sopenharmony_ci			setexposure(gspca_dev, gspca_dev->exposure->val);
3848c2ecf20Sopenharmony_ci		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
3858c2ecf20Sopenharmony_ci			setgain(gspca_dev, gspca_dev->gain->val);
3868c2ecf20Sopenharmony_ci		break;
3878c2ecf20Sopenharmony_ci	case V4L2_CID_HFLIP:
3888c2ecf20Sopenharmony_ci		sethvflip(gspca_dev, sd->hflip->val, 1);
3898c2ecf20Sopenharmony_ci		break;
3908c2ecf20Sopenharmony_ci	default:
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
3978c2ecf20Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
3988c2ecf20Sopenharmony_ci};
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/* this function is called at probe time */
4018c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4048c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
4078c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 5);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4108c2ecf20Sopenharmony_ci					V4L2_CID_CONTRAST, 0, 15, 1, 7);
4118c2ecf20Sopenharmony_ci	gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4128c2ecf20Sopenharmony_ci					V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
4138c2ecf20Sopenharmony_ci	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4148c2ecf20Sopenharmony_ci					V4L2_CID_EXPOSURE, 2, 63, 1,
4158c2ecf20Sopenharmony_ci					PAC7311_EXPOSURE_DEFAULT);
4168c2ecf20Sopenharmony_ci	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4178c2ecf20Sopenharmony_ci					V4L2_CID_GAIN, 0, 244, 1,
4188c2ecf20Sopenharmony_ci					PAC7311_GAIN_DEFAULT);
4198c2ecf20Sopenharmony_ci	sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4208c2ecf20Sopenharmony_ci		V4L2_CID_HFLIP, 0, 1, 1, 0);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (hdl->error) {
4238c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
4248c2ecf20Sopenharmony_ci		return hdl->error;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/* -- start the camera -- */
4328c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	sd->sof_read = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	reg_w_var(gspca_dev, start_7311,
4398c2ecf20Sopenharmony_ci		page4_7311, sizeof(page4_7311));
4408c2ecf20Sopenharmony_ci	setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
4418c2ecf20Sopenharmony_ci	setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain));
4428c2ecf20Sopenharmony_ci	setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
4438c2ecf20Sopenharmony_ci	sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* set correct resolution */
4468c2ecf20Sopenharmony_ci	switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
4478c2ecf20Sopenharmony_ci	case 2:					/* 160x120 */
4488c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0xff, 0x01);
4498c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x17, 0x20);
4508c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x87, 0x10);
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci	case 1:					/* 320x240 */
4538c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0xff, 0x01);
4548c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x17, 0x30);
4558c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x87, 0x11);
4568c2ecf20Sopenharmony_ci		break;
4578c2ecf20Sopenharmony_ci	case 0:					/* 640x480 */
4588c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0xff, 0x01);
4598c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x17, 0x00);
4608c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x87, 0x12);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	sd->sof_read = 0;
4658c2ecf20Sopenharmony_ci	sd->autogain_ignore_frames = 0;
4668c2ecf20Sopenharmony_ci	atomic_set(&sd->avg_lum, -1);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* start stream */
4698c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x01);
4708c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x78, 0x05);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x04);
4788c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x27, 0x80);
4798c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x28, 0xca);
4808c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x29, 0x53);
4818c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x2a, 0x0e);
4828c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0xff, 0x01);
4838c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x3e, 0x20);
4848c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
4858c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
4868c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void do_autogain(struct gspca_dev *gspca_dev)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4928c2ecf20Sopenharmony_ci	int avg_lum = atomic_read(&sd->avg_lum);
4938c2ecf20Sopenharmony_ci	int desired_lum, deadzone;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (avg_lum == -1)
4968c2ecf20Sopenharmony_ci		return;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	desired_lum = 170;
4998c2ecf20Sopenharmony_ci	deadzone = 20;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (sd->autogain_ignore_frames > 0)
5028c2ecf20Sopenharmony_ci		sd->autogain_ignore_frames--;
5038c2ecf20Sopenharmony_ci	else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
5048c2ecf20Sopenharmony_ci						    desired_lum, deadzone))
5058c2ecf20Sopenharmony_ci		sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/* JPEG header, part 1 */
5098c2ecf20Sopenharmony_cistatic const unsigned char pac_jpeg_header1[] = {
5108c2ecf20Sopenharmony_ci  0xff, 0xd8,		/* SOI: Start of Image */
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci  0xff, 0xc0,		/* SOF0: Start of Frame (Baseline DCT) */
5138c2ecf20Sopenharmony_ci  0x00, 0x11,		/* length = 17 bytes (including this length field) */
5148c2ecf20Sopenharmony_ci  0x08			/* Precision: 8 */
5158c2ecf20Sopenharmony_ci  /* 2 bytes is placed here: number of image lines */
5168c2ecf20Sopenharmony_ci  /* 2 bytes is placed here: samples per line */
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/* JPEG header, continued */
5208c2ecf20Sopenharmony_cistatic const unsigned char pac_jpeg_header2[] = {
5218c2ecf20Sopenharmony_ci  0x03,			/* Number of image components: 3 */
5228c2ecf20Sopenharmony_ci  0x01, 0x21, 0x00,	/* ID=1, Subsampling 1x1, Quantization table: 0 */
5238c2ecf20Sopenharmony_ci  0x02, 0x11, 0x01,	/* ID=2, Subsampling 2x1, Quantization table: 1 */
5248c2ecf20Sopenharmony_ci  0x03, 0x11, 0x01,	/* ID=3, Subsampling 2x1, Quantization table: 1 */
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci  0xff, 0xda,		/* SOS: Start Of Scan */
5278c2ecf20Sopenharmony_ci  0x00, 0x0c,		/* length = 12 bytes (including this length field) */
5288c2ecf20Sopenharmony_ci  0x03,			/* number of components: 3 */
5298c2ecf20Sopenharmony_ci  0x01, 0x00,		/* selector 1, table 0x00 */
5308c2ecf20Sopenharmony_ci  0x02, 0x11,		/* selector 2, table 0x11 */
5318c2ecf20Sopenharmony_ci  0x03, 0x11,		/* selector 3, table 0x11 */
5328c2ecf20Sopenharmony_ci  0x00, 0x3f,		/* Spectral selection: 0 .. 63 */
5338c2ecf20Sopenharmony_ci  0x00			/* Successive approximation: 0 */
5348c2ecf20Sopenharmony_ci};
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void pac_start_frame(struct gspca_dev *gspca_dev,
5378c2ecf20Sopenharmony_ci		__u16 lines, __u16 samples_per_line)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	unsigned char tmpbuf[4];
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, FIRST_PACKET,
5428c2ecf20Sopenharmony_ci		pac_jpeg_header1, sizeof(pac_jpeg_header1));
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	tmpbuf[0] = lines >> 8;
5458c2ecf20Sopenharmony_ci	tmpbuf[1] = lines & 0xff;
5468c2ecf20Sopenharmony_ci	tmpbuf[2] = samples_per_line >> 8;
5478c2ecf20Sopenharmony_ci	tmpbuf[3] = samples_per_line & 0xff;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET,
5508c2ecf20Sopenharmony_ci		tmpbuf, sizeof(tmpbuf));
5518c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET,
5528c2ecf20Sopenharmony_ci		pac_jpeg_header2, sizeof(pac_jpeg_header2));
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci/* this function is run at interrupt level */
5568c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
5578c2ecf20Sopenharmony_ci			u8 *data,			/* isoc packet */
5588c2ecf20Sopenharmony_ci			int len)			/* iso packet length */
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
5618c2ecf20Sopenharmony_ci	u8 *image;
5628c2ecf20Sopenharmony_ci	unsigned char *sof;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
5658c2ecf20Sopenharmony_ci	if (sof) {
5668c2ecf20Sopenharmony_ci		int n, lum_offset, footer_length;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		/*
5698c2ecf20Sopenharmony_ci		 * 6 bytes after the FF D9 EOF marker a number of lumination
5708c2ecf20Sopenharmony_ci		 * bytes are send corresponding to different parts of the
5718c2ecf20Sopenharmony_ci		 * image, the 14th and 15th byte after the EOF seem to
5728c2ecf20Sopenharmony_ci		 * correspond to the center of the image.
5738c2ecf20Sopenharmony_ci		 */
5748c2ecf20Sopenharmony_ci		lum_offset = 24 + sizeof pac_sof_marker;
5758c2ecf20Sopenharmony_ci		footer_length = 26;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		/* Finish decoding current frame */
5788c2ecf20Sopenharmony_ci		n = (sof - data) - (footer_length + sizeof pac_sof_marker);
5798c2ecf20Sopenharmony_ci		if (n < 0) {
5808c2ecf20Sopenharmony_ci			gspca_dev->image_len += n;
5818c2ecf20Sopenharmony_ci			n = 0;
5828c2ecf20Sopenharmony_ci		} else {
5838c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci		image = gspca_dev->image;
5868c2ecf20Sopenharmony_ci		if (image != NULL
5878c2ecf20Sopenharmony_ci		 && image[gspca_dev->image_len - 2] == 0xff
5888c2ecf20Sopenharmony_ci		 && image[gspca_dev->image_len - 1] == 0xd9)
5898c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		n = sof - data;
5928c2ecf20Sopenharmony_ci		len -= n;
5938c2ecf20Sopenharmony_ci		data = sof;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		/* Get average lumination */
5968c2ecf20Sopenharmony_ci		if (gspca_dev->last_packet_type == LAST_PACKET &&
5978c2ecf20Sopenharmony_ci				n >= lum_offset)
5988c2ecf20Sopenharmony_ci			atomic_set(&sd->avg_lum, data[-lum_offset] +
5998c2ecf20Sopenharmony_ci						data[-lum_offset + 1]);
6008c2ecf20Sopenharmony_ci		else
6018c2ecf20Sopenharmony_ci			atomic_set(&sd->avg_lum, -1);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		/* Start the new frame with the jpeg header */
6048c2ecf20Sopenharmony_ci		pac_start_frame(gspca_dev,
6058c2ecf20Sopenharmony_ci			gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
6118c2ecf20Sopenharmony_cistatic int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
6128c2ecf20Sopenharmony_ci			u8 *data,		/* interrupt packet data */
6138c2ecf20Sopenharmony_ci			int len)		/* interrupt packet length */
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	int ret = -EINVAL;
6168c2ecf20Sopenharmony_ci	u8 data0, data1;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (len == 2) {
6198c2ecf20Sopenharmony_ci		data0 = data[0];
6208c2ecf20Sopenharmony_ci		data1 = data[1];
6218c2ecf20Sopenharmony_ci		if ((data0 == 0x00 && data1 == 0x11) ||
6228c2ecf20Sopenharmony_ci		    (data0 == 0x22 && data1 == 0x33) ||
6238c2ecf20Sopenharmony_ci		    (data0 == 0x44 && data1 == 0x55) ||
6248c2ecf20Sopenharmony_ci		    (data0 == 0x66 && data1 == 0x77) ||
6258c2ecf20Sopenharmony_ci		    (data0 == 0x88 && data1 == 0x99) ||
6268c2ecf20Sopenharmony_ci		    (data0 == 0xaa && data1 == 0xbb) ||
6278c2ecf20Sopenharmony_ci		    (data0 == 0xcc && data1 == 0xdd) ||
6288c2ecf20Sopenharmony_ci		    (data0 == 0xee && data1 == 0xff)) {
6298c2ecf20Sopenharmony_ci			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
6308c2ecf20Sopenharmony_ci			input_sync(gspca_dev->input_dev);
6318c2ecf20Sopenharmony_ci			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
6328c2ecf20Sopenharmony_ci			input_sync(gspca_dev->input_dev);
6338c2ecf20Sopenharmony_ci			ret = 0;
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	return ret;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci#endif
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
6428c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
6438c2ecf20Sopenharmony_ci	.config = sd_config,
6448c2ecf20Sopenharmony_ci	.init = sd_init,
6458c2ecf20Sopenharmony_ci	.init_controls = sd_init_controls,
6468c2ecf20Sopenharmony_ci	.start = sd_start,
6478c2ecf20Sopenharmony_ci	.stopN = sd_stopN,
6488c2ecf20Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
6498c2ecf20Sopenharmony_ci	.dq_callback = do_autogain,
6508c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
6518c2ecf20Sopenharmony_ci	.int_pkt_scan = sd_int_pkt_scan,
6528c2ecf20Sopenharmony_ci#endif
6538c2ecf20Sopenharmony_ci};
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/* -- module initialisation -- */
6568c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
6578c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x2600)},
6588c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x2601)},
6598c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x2603)},
6608c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x2608)},
6618c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x260e)},
6628c2ecf20Sopenharmony_ci	{USB_DEVICE(0x093a, 0x260f)},
6638c2ecf20Sopenharmony_ci	{}
6648c2ecf20Sopenharmony_ci};
6658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci/* -- device connect -- */
6688c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
6698c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
6728c2ecf20Sopenharmony_ci				THIS_MODULE);
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
6768c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
6778c2ecf20Sopenharmony_ci	.id_table = device_table,
6788c2ecf20Sopenharmony_ci	.probe = sd_probe,
6798c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
6808c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
6818c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
6828c2ecf20Sopenharmony_ci	.resume = gspca_resume,
6838c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
6848c2ecf20Sopenharmony_ci#endif
6858c2ecf20Sopenharmony_ci};
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
688