18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Syntek DV4000 (STK014) subdriver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define MODULE_NAME "stk014"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "gspca.h"
138c2ecf20Sopenharmony_ci#include "jpeg.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define QUALITY 50
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* specific webcam descriptor */
228c2ecf20Sopenharmony_cistruct sd {
238c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev;	/* !! must be the first item */
248c2ecf20Sopenharmony_ci	u8 jpeg_hdr[JPEG_HDR_SZ];
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = {
288c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
298c2ecf20Sopenharmony_ci		.bytesperline = 320,
308c2ecf20Sopenharmony_ci		.sizeimage = 320 * 240 * 3 / 8 + 590,
318c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
328c2ecf20Sopenharmony_ci		.priv = 1},
338c2ecf20Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
348c2ecf20Sopenharmony_ci		.bytesperline = 640,
358c2ecf20Sopenharmony_ci		.sizeimage = 640 * 480 * 3 / 8 + 590,
368c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_JPEG,
378c2ecf20Sopenharmony_ci		.priv = 0},
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* -- read a register -- */
418c2ecf20Sopenharmony_cistatic u8 reg_r(struct gspca_dev *gspca_dev,
428c2ecf20Sopenharmony_ci			__u16 index)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
458c2ecf20Sopenharmony_ci	int ret;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
488c2ecf20Sopenharmony_ci		return 0;
498c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
508c2ecf20Sopenharmony_ci			0x00,
518c2ecf20Sopenharmony_ci			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
528c2ecf20Sopenharmony_ci			0x00,
538c2ecf20Sopenharmony_ci			index,
548c2ecf20Sopenharmony_ci			gspca_dev->usb_buf, 1,
558c2ecf20Sopenharmony_ci			500);
568c2ecf20Sopenharmony_ci	if (ret < 0) {
578c2ecf20Sopenharmony_ci		pr_err("reg_r err %d\n", ret);
588c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
598c2ecf20Sopenharmony_ci		return 0;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	return gspca_dev->usb_buf[0];
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* -- write a register -- */
658c2ecf20Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev,
668c2ecf20Sopenharmony_ci			__u16 index, __u16 value)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
698c2ecf20Sopenharmony_ci	int ret;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
728c2ecf20Sopenharmony_ci		return;
738c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
748c2ecf20Sopenharmony_ci			0x01,
758c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
768c2ecf20Sopenharmony_ci			value,
778c2ecf20Sopenharmony_ci			index,
788c2ecf20Sopenharmony_ci			NULL,
798c2ecf20Sopenharmony_ci			0,
808c2ecf20Sopenharmony_ci			500);
818c2ecf20Sopenharmony_ci	if (ret < 0) {
828c2ecf20Sopenharmony_ci		pr_err("reg_w err %d\n", ret);
838c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* -- get a bulk value (4 bytes) -- */
888c2ecf20Sopenharmony_cistatic void rcv_val(struct gspca_dev *gspca_dev,
898c2ecf20Sopenharmony_ci			int ads)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
928c2ecf20Sopenharmony_ci	int alen, ret;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff);
958c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff);
968c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x636, ads & 0xff);
978c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x637, 0);
988c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x638, 4);	/* len & 0xff */
998c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x639, 0);	/* len >> 8 */
1008c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x63a, 0);
1018c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x63b, 0);
1028c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x630, 5);
1038c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
1048c2ecf20Sopenharmony_ci		return;
1058c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(dev,
1068c2ecf20Sopenharmony_ci			usb_rcvbulkpipe(dev, 0x05),
1078c2ecf20Sopenharmony_ci			gspca_dev->usb_buf,
1088c2ecf20Sopenharmony_ci			4,		/* length */
1098c2ecf20Sopenharmony_ci			&alen,
1108c2ecf20Sopenharmony_ci			500);		/* timeout in milliseconds */
1118c2ecf20Sopenharmony_ci	if (ret < 0) {
1128c2ecf20Sopenharmony_ci		pr_err("rcv_val err %d\n", ret);
1138c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* -- send a bulk value -- */
1188c2ecf20Sopenharmony_cistatic void snd_val(struct gspca_dev *gspca_dev,
1198c2ecf20Sopenharmony_ci			int ads,
1208c2ecf20Sopenharmony_ci			unsigned int val)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
1238c2ecf20Sopenharmony_ci	int alen, ret;
1248c2ecf20Sopenharmony_ci	__u8 seq = 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (ads == 0x003f08) {
1278c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x0704);
1288c2ecf20Sopenharmony_ci		seq = reg_r(gspca_dev, 0x0705);
1298c2ecf20Sopenharmony_ci		reg_r(gspca_dev, 0x0650);
1308c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x654, seq);
1318c2ecf20Sopenharmony_ci	} else {
1328c2ecf20Sopenharmony_ci		reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff);
1358c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x656, ads & 0xff);
1368c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x657, 0);
1378c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x658, 0x04);	/* size */
1388c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x659, 0);
1398c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x65a, 0);
1408c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x65b, 0);
1418c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x650, 5);
1428c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err < 0)
1438c2ecf20Sopenharmony_ci		return;
1448c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = val >> 24;
1458c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = val >> 16;
1468c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[2] = val >> 8;
1478c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[3] = val;
1488c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(dev,
1498c2ecf20Sopenharmony_ci			usb_sndbulkpipe(dev, 6),
1508c2ecf20Sopenharmony_ci			gspca_dev->usb_buf,
1518c2ecf20Sopenharmony_ci			4,
1528c2ecf20Sopenharmony_ci			&alen,
1538c2ecf20Sopenharmony_ci			500);	/* timeout in milliseconds */
1548c2ecf20Sopenharmony_ci	if (ret < 0) {
1558c2ecf20Sopenharmony_ci		pr_err("snd_val err %d\n", ret);
1568c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
1578c2ecf20Sopenharmony_ci	} else {
1588c2ecf20Sopenharmony_ci		if (ads == 0x003f08) {
1598c2ecf20Sopenharmony_ci			seq += 4;
1608c2ecf20Sopenharmony_ci			seq &= 0x3f;
1618c2ecf20Sopenharmony_ci			reg_w(gspca_dev, 0x705, seq);
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/* set a camera parameter */
1678c2ecf20Sopenharmony_cistatic void set_par(struct gspca_dev *gspca_dev,
1688c2ecf20Sopenharmony_ci		   int parval)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	snd_val(gspca_dev, 0x003f08, parval);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int parval;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	parval = 0x06000000		/* whiteness */
1788c2ecf20Sopenharmony_ci		+ (val << 16);
1798c2ecf20Sopenharmony_ci	set_par(gspca_dev, parval);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void setcontrast(struct gspca_dev *gspca_dev, s32 val)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	int parval;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	parval = 0x07000000		/* contrast */
1878c2ecf20Sopenharmony_ci		+ (val << 16);
1888c2ecf20Sopenharmony_ci	set_par(gspca_dev, parval);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void setcolors(struct gspca_dev *gspca_dev, s32 val)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int parval;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	parval = 0x08000000		/* saturation */
1968c2ecf20Sopenharmony_ci		+ (val << 16);
1978c2ecf20Sopenharmony_ci	set_par(gspca_dev, parval);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	set_par(gspca_dev, val == 1
2038c2ecf20Sopenharmony_ci			? 0x33640000		/* 50 Hz */
2048c2ecf20Sopenharmony_ci			: 0x33780000);		/* 60 Hz */
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* this function is called at probe time */
2088c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
2098c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	gspca_dev->cam.cam_mode = vga_mode;
2128c2ecf20Sopenharmony_ci	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
2178c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	u8 ret;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* check if the device responds */
2228c2ecf20Sopenharmony_ci	usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
2238c2ecf20Sopenharmony_ci	ret = reg_r(gspca_dev, 0x0740);
2248c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err >= 0) {
2258c2ecf20Sopenharmony_ci		if (ret != 0xff) {
2268c2ecf20Sopenharmony_ci			pr_err("init reg: 0x%02x\n", ret);
2278c2ecf20Sopenharmony_ci			gspca_dev->usb_err = -EIO;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci/* -- start the camera -- */
2348c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
2378c2ecf20Sopenharmony_ci	int ret, value;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* create the JPEG header */
2408c2ecf20Sopenharmony_ci	jpeg_define(sd->jpeg_hdr, gspca_dev->pixfmt.height,
2418c2ecf20Sopenharmony_ci			gspca_dev->pixfmt.width,
2428c2ecf20Sopenharmony_ci			0x22);		/* JPEG 411 */
2438c2ecf20Sopenharmony_ci	jpeg_set_qual(sd->jpeg_hdr, QUALITY);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* work on alternate 1 */
2468c2ecf20Sopenharmony_ci	usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x10000000);
2498c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x00000000);
2508c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x8002e001);
2518c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x14000000);
2528c2ecf20Sopenharmony_ci	if (gspca_dev->pixfmt.width > 320)
2538c2ecf20Sopenharmony_ci		value = 0x8002e001;		/* 640x480 */
2548c2ecf20Sopenharmony_ci	else
2558c2ecf20Sopenharmony_ci		value = 0x4001f000;		/* 320x240 */
2568c2ecf20Sopenharmony_ci	set_par(gspca_dev, value);
2578c2ecf20Sopenharmony_ci	ret = usb_set_interface(gspca_dev->dev,
2588c2ecf20Sopenharmony_ci					gspca_dev->iface,
2598c2ecf20Sopenharmony_ci					gspca_dev->alt);
2608c2ecf20Sopenharmony_ci	if (ret < 0) {
2618c2ecf20Sopenharmony_ci		pr_err("set intf %d %d failed\n",
2628c2ecf20Sopenharmony_ci		       gspca_dev->iface, gspca_dev->alt);
2638c2ecf20Sopenharmony_ci		gspca_dev->usb_err = ret;
2648c2ecf20Sopenharmony_ci		goto out;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x0630);
2678c2ecf20Sopenharmony_ci	rcv_val(gspca_dev, 0x000020);	/* << (value ff ff ff ff) */
2688c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x0650);
2698c2ecf20Sopenharmony_ci	snd_val(gspca_dev, 0x000020, 0xffffffff);
2708c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0620, 0);
2718c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0630, 0);
2728c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0640, 0);
2738c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0650, 0);
2748c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0660, 0);
2758c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x09800000);		/* Red ? */
2768c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x0a800000);		/* Green ? */
2778c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x0b800000);		/* Blue ? */
2788c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x0d030000);		/* Gamma ? */
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* start the video flow */
2818c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x01000000);
2828c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x01000000);
2838c2ecf20Sopenharmony_ci	if (gspca_dev->usb_err >= 0)
2848c2ecf20Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "camera started alt: 0x%02x\n",
2858c2ecf20Sopenharmony_ci			  gspca_dev->alt);
2868c2ecf20Sopenharmony_ciout:
2878c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct usb_device *dev = gspca_dev->dev;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x02000000);
2958c2ecf20Sopenharmony_ci	set_par(gspca_dev, 0x02000000);
2968c2ecf20Sopenharmony_ci	usb_set_interface(dev, gspca_dev->iface, 1);
2978c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x0630);
2988c2ecf20Sopenharmony_ci	rcv_val(gspca_dev, 0x000020);	/* << (value ff ff ff ff) */
2998c2ecf20Sopenharmony_ci	reg_r(gspca_dev, 0x0650);
3008c2ecf20Sopenharmony_ci	snd_val(gspca_dev, 0x000020, 0xffffffff);
3018c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0620, 0);
3028c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0630, 0);
3038c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0640, 0);
3048c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0650, 0);
3058c2ecf20Sopenharmony_ci	reg_w(gspca_dev, 0x0660, 0);
3068c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_STREAM, "camera stopped\n");
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
3108c2ecf20Sopenharmony_ci			u8 *data,			/* isoc packet */
3118c2ecf20Sopenharmony_ci			int len)			/* iso packet length */
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
3148c2ecf20Sopenharmony_ci	static unsigned char ffd9[] = {0xff, 0xd9};
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* a frame starts with:
3178c2ecf20Sopenharmony_ci	 *	- 0xff 0xfe
3188c2ecf20Sopenharmony_ci	 *	- 0x08 0x00	- length (little endian ?!)
3198c2ecf20Sopenharmony_ci	 *	- 4 bytes = size of whole frame (BE - including header)
3208c2ecf20Sopenharmony_ci	 *	- 0x00 0x0c
3218c2ecf20Sopenharmony_ci	 *	- 0xff 0xd8
3228c2ecf20Sopenharmony_ci	 *	- ..	JPEG image with escape sequences (ff 00)
3238c2ecf20Sopenharmony_ci	 *		(without ending - ff d9)
3248c2ecf20Sopenharmony_ci	 */
3258c2ecf20Sopenharmony_ci	if (data[0] == 0xff && data[1] == 0xfe) {
3268c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET,
3278c2ecf20Sopenharmony_ci				ffd9, 2);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		/* put the JPEG 411 header */
3308c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET,
3318c2ecf20Sopenharmony_ci			sd->jpeg_hdr, JPEG_HDR_SZ);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		/* beginning of the frame */
3348c2ecf20Sopenharmony_ci#define STKHDRSZ 12
3358c2ecf20Sopenharmony_ci		data += STKHDRSZ;
3368c2ecf20Sopenharmony_ci		len -= STKHDRSZ;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
3448c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	gspca_dev->usb_err = 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming)
3498c2ecf20Sopenharmony_ci		return 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	switch (ctrl->id) {
3528c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
3538c2ecf20Sopenharmony_ci		setbrightness(gspca_dev, ctrl->val);
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
3568c2ecf20Sopenharmony_ci		setcontrast(gspca_dev, ctrl->val);
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
3598c2ecf20Sopenharmony_ci		setcolors(gspca_dev, ctrl->val);
3608c2ecf20Sopenharmony_ci		break;
3618c2ecf20Sopenharmony_ci	case V4L2_CID_POWER_LINE_FREQUENCY:
3628c2ecf20Sopenharmony_ci		setlightfreq(gspca_dev, ctrl->val);
3638c2ecf20Sopenharmony_ci		break;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
3698c2ecf20Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
3778c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 4);
3788c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
3798c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
3808c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
3818c2ecf20Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 255, 1, 127);
3828c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
3838c2ecf20Sopenharmony_ci			V4L2_CID_SATURATION, 0, 255, 1, 127);
3848c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
3858c2ecf20Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY,
3868c2ecf20Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
3878c2ecf20Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (hdl->error) {
3908c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
3918c2ecf20Sopenharmony_ci		return hdl->error;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	return 0;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci/* sub-driver description */
3978c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
3988c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
3998c2ecf20Sopenharmony_ci	.config = sd_config,
4008c2ecf20Sopenharmony_ci	.init = sd_init,
4018c2ecf20Sopenharmony_ci	.init_controls = sd_init_controls,
4028c2ecf20Sopenharmony_ci	.start = sd_start,
4038c2ecf20Sopenharmony_ci	.stopN = sd_stopN,
4048c2ecf20Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
4058c2ecf20Sopenharmony_ci};
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/* -- module initialisation -- */
4088c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
4098c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05e1, 0x0893)},
4108c2ecf20Sopenharmony_ci	{}
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/* -- device connect -- */
4158c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
4168c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
4198c2ecf20Sopenharmony_ci				THIS_MODULE);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
4238c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
4248c2ecf20Sopenharmony_ci	.id_table = device_table,
4258c2ecf20Sopenharmony_ci	.probe = sd_probe,
4268c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
4278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4288c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
4298c2ecf20Sopenharmony_ci	.resume = gspca_resume,
4308c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
4318c2ecf20Sopenharmony_ci#endif
4328c2ecf20Sopenharmony_ci};
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
435