xref: /linux/drivers/media/usb/gspca/jeilinj.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23040b043STheodore Kilgore /*
33040b043STheodore Kilgore  * Jeilinj subdriver
43040b043STheodore Kilgore  *
53040b043STheodore Kilgore  * Supports some Jeilin dual-mode cameras which use bulk transport and
63040b043STheodore Kilgore  * download raw JPEG data.
73040b043STheodore Kilgore  *
83040b043STheodore Kilgore  * Copyright (C) 2009 Theodore Kilgore
95396e62fSPatrice Chotard  *
105396e62fSPatrice Chotard  * Sportscam DV15 support and control settings are
11c3d86927SPatrice Chotard  * Copyright (C) 2011 Patrice Chotard
123040b043STheodore Kilgore  */
133040b043STheodore Kilgore 
14133a9fe9SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15133a9fe9SJoe Perches 
163040b043STheodore Kilgore #define MODULE_NAME "jeilinj"
173040b043STheodore Kilgore 
185a0e3ad6STejun Heo #include <linux/slab.h>
193040b043STheodore Kilgore #include "gspca.h"
203040b043STheodore Kilgore #include "jpeg.h"
213040b043STheodore Kilgore 
223040b043STheodore Kilgore MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
233040b043STheodore Kilgore MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
243040b043STheodore Kilgore MODULE_LICENSE("GPL");
253040b043STheodore Kilgore 
263040b043STheodore Kilgore /* Default timeouts, in ms */
273040b043STheodore Kilgore #define JEILINJ_CMD_TIMEOUT 500
28713b466fSPatrice Chotard #define JEILINJ_CMD_DELAY 160
293040b043STheodore Kilgore #define JEILINJ_DATA_TIMEOUT 1000
303040b043STheodore Kilgore 
313040b043STheodore Kilgore /* Maximum transfer size to use. */
323040b043STheodore Kilgore #define JEILINJ_MAX_TRANSFER 0x200
333040b043STheodore Kilgore #define FRAME_HEADER_LEN 0x10
34c3d86927SPatrice Chotard #define FRAME_START 0xFFFFFFFF
353040b043STheodore Kilgore 
36713b466fSPatrice Chotard enum {
37713b466fSPatrice Chotard 	SAKAR_57379,
38713b466fSPatrice Chotard 	SPORTSCAM_DV15,
39713b466fSPatrice Chotard };
405396e62fSPatrice Chotard 
415396e62fSPatrice Chotard #define CAMQUALITY_MIN 0	/* highest cam quality */
425396e62fSPatrice Chotard #define CAMQUALITY_MAX 97	/* lowest cam quality  */
435396e62fSPatrice Chotard 
443040b043STheodore Kilgore /* Structure to hold all of our device specific stuff */
453040b043STheodore Kilgore struct sd {
463040b043STheodore Kilgore 	struct gspca_dev gspca_dev;	/* !! must be the first item */
47c3d86927SPatrice Chotard 	int blocks_left;
483040b043STheodore Kilgore 	const struct v4l2_pix_format *cap_mode;
49bfaab899SHans Verkuil 	struct v4l2_ctrl *freq;
50bfaab899SHans Verkuil 	struct v4l2_ctrl *jpegqual;
513040b043STheodore Kilgore 	/* Driver stuff */
52713b466fSPatrice Chotard 	u8 type;
533040b043STheodore Kilgore 	u8 quality;				 /* image quality */
545396e62fSPatrice Chotard #define QUALITY_MIN 35
555396e62fSPatrice Chotard #define QUALITY_MAX 85
565396e62fSPatrice Chotard #define QUALITY_DEF 85
579a731a32SJean-François Moine 	u8 jpeg_hdr[JPEG_HDR_SZ];
583040b043STheodore Kilgore };
593040b043STheodore Kilgore 
603040b043STheodore Kilgore struct jlj_command {
613040b043STheodore Kilgore 	unsigned char instruction[2];
623040b043STheodore Kilgore 	unsigned char ack_wanted;
63713b466fSPatrice Chotard 	unsigned char delay;
643040b043STheodore Kilgore };
653040b043STheodore Kilgore 
663040b043STheodore Kilgore /* AFAICT these cameras will only do 320x240. */
673040b043STheodore Kilgore static struct v4l2_pix_format jlj_mode[] = {
683040b043STheodore Kilgore 	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
693040b043STheodore Kilgore 		.bytesperline = 320,
703040b043STheodore Kilgore 		.sizeimage = 320 * 240,
713040b043STheodore Kilgore 		.colorspace = V4L2_COLORSPACE_JPEG,
726f8efcfbSPatrice Chotard 		.priv = 0},
736f8efcfbSPatrice Chotard 	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
746f8efcfbSPatrice Chotard 		.bytesperline = 640,
756f8efcfbSPatrice Chotard 		.sizeimage = 640 * 480,
766f8efcfbSPatrice Chotard 		.colorspace = V4L2_COLORSPACE_JPEG,
773040b043STheodore Kilgore 		.priv = 0}
783040b043STheodore Kilgore };
793040b043STheodore Kilgore 
803040b043STheodore Kilgore /*
813040b043STheodore Kilgore  * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
823040b043STheodore Kilgore  * and 0x82 for bulk transfer.
833040b043STheodore Kilgore  */
843040b043STheodore Kilgore 
853040b043STheodore Kilgore /* All commands are two bytes only */
868715b16eSPatrice Chotard static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
873040b043STheodore Kilgore {
883040b043STheodore Kilgore 	int retval;
893040b043STheodore Kilgore 
908715b16eSPatrice Chotard 	if (gspca_dev->usb_err < 0)
918715b16eSPatrice Chotard 		return;
923040b043STheodore Kilgore 	memcpy(gspca_dev->usb_buf, command, 2);
933040b043STheodore Kilgore 	retval = usb_bulk_msg(gspca_dev->dev,
943040b043STheodore Kilgore 			usb_sndbulkpipe(gspca_dev->dev, 3),
953040b043STheodore Kilgore 			gspca_dev->usb_buf, 2, NULL, 500);
968715b16eSPatrice Chotard 	if (retval < 0) {
97133a9fe9SJoe Perches 		pr_err("command write [%02x] error %d\n",
983040b043STheodore Kilgore 		       gspca_dev->usb_buf[0], retval);
998715b16eSPatrice Chotard 		gspca_dev->usb_err = retval;
1008715b16eSPatrice Chotard 	}
1013040b043STheodore Kilgore }
1023040b043STheodore Kilgore 
1033040b043STheodore Kilgore /* Responses are one byte only */
1049825f376SMauro Carvalho Chehab static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response)
1053040b043STheodore Kilgore {
1063040b043STheodore Kilgore 	int retval;
1073040b043STheodore Kilgore 
1088715b16eSPatrice Chotard 	if (gspca_dev->usb_err < 0)
1098715b16eSPatrice Chotard 		return;
1103040b043STheodore Kilgore 	retval = usb_bulk_msg(gspca_dev->dev,
1113040b043STheodore Kilgore 	usb_rcvbulkpipe(gspca_dev->dev, 0x84),
1123040b043STheodore Kilgore 				gspca_dev->usb_buf, 1, NULL, 500);
1139825f376SMauro Carvalho Chehab 	*response = gspca_dev->usb_buf[0];
1148715b16eSPatrice Chotard 	if (retval < 0) {
115133a9fe9SJoe Perches 		pr_err("read command [%02x] error %d\n",
1163040b043STheodore Kilgore 		       gspca_dev->usb_buf[0], retval);
1178715b16eSPatrice Chotard 		gspca_dev->usb_err = retval;
1188715b16eSPatrice Chotard 	}
1193040b043STheodore Kilgore }
1203040b043STheodore Kilgore 
121bfaab899SHans Verkuil static void setfreq(struct gspca_dev *gspca_dev, s32 val)
1225396e62fSPatrice Chotard {
1235396e62fSPatrice Chotard 	u8 freq_commands[][2] = {
1245396e62fSPatrice Chotard 		{0x71, 0x80},
1255396e62fSPatrice Chotard 		{0x70, 0x07}
1265396e62fSPatrice Chotard 	};
1275396e62fSPatrice Chotard 
128bfaab899SHans Verkuil 	freq_commands[0][1] |= val >> 1;
1295396e62fSPatrice Chotard 
1305396e62fSPatrice Chotard 	jlj_write2(gspca_dev, freq_commands[0]);
1315396e62fSPatrice Chotard 	jlj_write2(gspca_dev, freq_commands[1]);
1325396e62fSPatrice Chotard }
1335396e62fSPatrice Chotard 
134bfaab899SHans Verkuil static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
1355396e62fSPatrice Chotard {
1365396e62fSPatrice Chotard 	u8 quality_commands[][2] = {
1375396e62fSPatrice Chotard 		{0x71, 0x1E},
1385396e62fSPatrice Chotard 		{0x70, 0x06}
1395396e62fSPatrice Chotard 	};
1405396e62fSPatrice Chotard 	u8 camquality;
1415396e62fSPatrice Chotard 
1425396e62fSPatrice Chotard 	/* adapt camera quality from jpeg quality */
143bfaab899SHans Verkuil 	camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
1445396e62fSPatrice Chotard 		/ (QUALITY_MAX - QUALITY_MIN);
1455396e62fSPatrice Chotard 	quality_commands[0][1] += camquality;
1465396e62fSPatrice Chotard 
1475396e62fSPatrice Chotard 	jlj_write2(gspca_dev, quality_commands[0]);
1485396e62fSPatrice Chotard 	jlj_write2(gspca_dev, quality_commands[1]);
1495396e62fSPatrice Chotard }
1505396e62fSPatrice Chotard 
151bfaab899SHans Verkuil static void setautogain(struct gspca_dev *gspca_dev, s32 val)
1525396e62fSPatrice Chotard {
1535396e62fSPatrice Chotard 	u8 autogain_commands[][2] = {
1545396e62fSPatrice Chotard 		{0x94, 0x02},
1555396e62fSPatrice Chotard 		{0xcf, 0x00}
1565396e62fSPatrice Chotard 	};
1575396e62fSPatrice Chotard 
158bfaab899SHans Verkuil 	autogain_commands[1][1] = val << 4;
1595396e62fSPatrice Chotard 
1605396e62fSPatrice Chotard 	jlj_write2(gspca_dev, autogain_commands[0]);
1615396e62fSPatrice Chotard 	jlj_write2(gspca_dev, autogain_commands[1]);
1625396e62fSPatrice Chotard }
1635396e62fSPatrice Chotard 
164bfaab899SHans Verkuil static void setred(struct gspca_dev *gspca_dev, s32 val)
1655396e62fSPatrice Chotard {
1665396e62fSPatrice Chotard 	u8 setred_commands[][2] = {
1675396e62fSPatrice Chotard 		{0x94, 0x02},
1685396e62fSPatrice Chotard 		{0xe6, 0x00}
1695396e62fSPatrice Chotard 	};
1705396e62fSPatrice Chotard 
171bfaab899SHans Verkuil 	setred_commands[1][1] = val;
1725396e62fSPatrice Chotard 
1735396e62fSPatrice Chotard 	jlj_write2(gspca_dev, setred_commands[0]);
1745396e62fSPatrice Chotard 	jlj_write2(gspca_dev, setred_commands[1]);
1755396e62fSPatrice Chotard }
1765396e62fSPatrice Chotard 
177bfaab899SHans Verkuil static void setgreen(struct gspca_dev *gspca_dev, s32 val)
1785396e62fSPatrice Chotard {
1795396e62fSPatrice Chotard 	u8 setgreen_commands[][2] = {
1805396e62fSPatrice Chotard 		{0x94, 0x02},
1815396e62fSPatrice Chotard 		{0xe7, 0x00}
1825396e62fSPatrice Chotard 	};
1835396e62fSPatrice Chotard 
184bfaab899SHans Verkuil 	setgreen_commands[1][1] = val;
1855396e62fSPatrice Chotard 
1865396e62fSPatrice Chotard 	jlj_write2(gspca_dev, setgreen_commands[0]);
1875396e62fSPatrice Chotard 	jlj_write2(gspca_dev, setgreen_commands[1]);
1885396e62fSPatrice Chotard }
1895396e62fSPatrice Chotard 
190bfaab899SHans Verkuil static void setblue(struct gspca_dev *gspca_dev, s32 val)
1915396e62fSPatrice Chotard {
1925396e62fSPatrice Chotard 	u8 setblue_commands[][2] = {
1935396e62fSPatrice Chotard 		{0x94, 0x02},
1945396e62fSPatrice Chotard 		{0xe9, 0x00}
1955396e62fSPatrice Chotard 	};
1965396e62fSPatrice Chotard 
197bfaab899SHans Verkuil 	setblue_commands[1][1] = val;
1985396e62fSPatrice Chotard 
1995396e62fSPatrice Chotard 	jlj_write2(gspca_dev, setblue_commands[0]);
2005396e62fSPatrice Chotard 	jlj_write2(gspca_dev, setblue_commands[1]);
2015396e62fSPatrice Chotard }
2025396e62fSPatrice Chotard 
2033040b043STheodore Kilgore static int jlj_start(struct gspca_dev *gspca_dev)
2043040b043STheodore Kilgore {
2053040b043STheodore Kilgore 	int i;
206713b466fSPatrice Chotard 	int start_commands_size;
2073040b043STheodore Kilgore 	u8 response = 0xff;
208c3d86927SPatrice Chotard 	struct sd *sd = (struct sd *) gspca_dev;
2093040b043STheodore Kilgore 	struct jlj_command start_commands[] = {
210713b466fSPatrice Chotard 		{{0x71, 0x81}, 0, 0},
211713b466fSPatrice Chotard 		{{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
212713b466fSPatrice Chotard 		{{0x95, 0x70}, 1, 0},
213713b466fSPatrice Chotard 		{{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
214713b466fSPatrice Chotard 		{{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
215713b466fSPatrice Chotard 		{{0x95, 0x70}, 1, 0},
216713b466fSPatrice Chotard 		{{0x71, 0x00}, 0, 0},   /* start streaming ??*/
217713b466fSPatrice Chotard 		{{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
218713b466fSPatrice Chotard 		{{0x95, 0x70}, 1, 0},
219713b466fSPatrice Chotard #define SPORTSCAM_DV15_CMD_SIZE 9
220713b466fSPatrice Chotard 		{{0x94, 0x02}, 0, 0},
221713b466fSPatrice Chotard 		{{0xde, 0x24}, 0, 0},
222713b466fSPatrice Chotard 		{{0x94, 0x02}, 0, 0},
223713b466fSPatrice Chotard 		{{0xdd, 0xf0}, 0, 0},
224713b466fSPatrice Chotard 		{{0x94, 0x02}, 0, 0},
225713b466fSPatrice Chotard 		{{0xe3, 0x2c}, 0, 0},
226713b466fSPatrice Chotard 		{{0x94, 0x02}, 0, 0},
227713b466fSPatrice Chotard 		{{0xe4, 0x00}, 0, 0},
228713b466fSPatrice Chotard 		{{0x94, 0x02}, 0, 0},
229713b466fSPatrice Chotard 		{{0xe5, 0x00}, 0, 0},
230713b466fSPatrice Chotard 		{{0x94, 0x02}, 0, 0},
231713b466fSPatrice Chotard 		{{0xe6, 0x2c}, 0, 0},
232713b466fSPatrice Chotard 		{{0x94, 0x03}, 0, 0},
2335396e62fSPatrice Chotard 		{{0xaa, 0x00}, 0, 0}
2343040b043STheodore Kilgore 	};
235c3d86927SPatrice Chotard 
236c3d86927SPatrice Chotard 	sd->blocks_left = 0;
237713b466fSPatrice Chotard 	/* Under Windows, USB spy shows that only the 9 first start
238713b466fSPatrice Chotard 	 * commands are used for SPORTSCAM_DV15 webcam
239713b466fSPatrice Chotard 	 */
240713b466fSPatrice Chotard 	if (sd->type == SPORTSCAM_DV15)
241713b466fSPatrice Chotard 		start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
242713b466fSPatrice Chotard 	else
243713b466fSPatrice Chotard 		start_commands_size = ARRAY_SIZE(start_commands);
244713b466fSPatrice Chotard 
245713b466fSPatrice Chotard 	for (i = 0; i < start_commands_size; i++) {
2468715b16eSPatrice Chotard 		jlj_write2(gspca_dev, start_commands[i].instruction);
247713b466fSPatrice Chotard 		if (start_commands[i].delay)
248713b466fSPatrice Chotard 			msleep(start_commands[i].delay);
2493040b043STheodore Kilgore 		if (start_commands[i].ack_wanted)
2509825f376SMauro Carvalho Chehab 			jlj_read1(gspca_dev, &response);
2513040b043STheodore Kilgore 	}
252bfaab899SHans Verkuil 	setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
2535396e62fSPatrice Chotard 	msleep(2);
254bfaab899SHans Verkuil 	setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
2558715b16eSPatrice Chotard 	if (gspca_dev->usb_err < 0)
25652173c5fSJoe Perches 		gspca_err(gspca_dev, "Start streaming command failed\n");
2578715b16eSPatrice Chotard 	return gspca_dev->usb_err;
2583040b043STheodore Kilgore }
2593040b043STheodore Kilgore 
260c3d86927SPatrice Chotard static void sd_pkt_scan(struct gspca_dev *gspca_dev,
261c3d86927SPatrice Chotard 			u8 *data, int len)
2623040b043STheodore Kilgore {
263c3d86927SPatrice Chotard 	struct sd *sd = (struct sd *) gspca_dev;
2643040b043STheodore Kilgore 	int packet_type;
265c3d86927SPatrice Chotard 	u32 header_marker;
2663040b043STheodore Kilgore 
26737d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Got %d bytes out of %d for Block 0\n",
268c3d86927SPatrice Chotard 		  len, JEILINJ_MAX_TRANSFER);
269c3d86927SPatrice Chotard 	if (len != JEILINJ_MAX_TRANSFER) {
27037d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PACK, "bad length\n");
271c3d86927SPatrice Chotard 		goto discard;
2723040b043STheodore Kilgore 	}
273c3d86927SPatrice Chotard 	/* check if it's start of frame */
274c3d86927SPatrice Chotard 	header_marker = ((u32 *)data)[0];
275c3d86927SPatrice Chotard 	if (header_marker == FRAME_START) {
276c3d86927SPatrice Chotard 		sd->blocks_left = data[0x0a] - 1;
27737d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "blocks_left = 0x%x\n",
27837d5efb0SJoe Perches 			  sd->blocks_left);
2796ca3f255SHans de Goede 		/* Start a new frame, and add the JPEG header, first thing */
28076dd272bSJean-Francois Moine 		gspca_frame_add(gspca_dev, FIRST_PACKET,
281c3d86927SPatrice Chotard 				sd->jpeg_hdr, JPEG_HDR_SZ);
2823040b043STheodore Kilgore 		/* Toss line 0 of data block 0, keep the rest. */
2836ca3f255SHans de Goede 		gspca_frame_add(gspca_dev, INTER_PACKET,
284c3d86927SPatrice Chotard 				data + FRAME_HEADER_LEN,
2853040b043STheodore Kilgore 				JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
286c3d86927SPatrice Chotard 	} else if (sd->blocks_left > 0) {
28737d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "%d blocks remaining for frame\n",
288c3d86927SPatrice Chotard 			  sd->blocks_left);
289c3d86927SPatrice Chotard 		sd->blocks_left -= 1;
290c3d86927SPatrice Chotard 		if (sd->blocks_left == 0)
2913040b043STheodore Kilgore 			packet_type = LAST_PACKET;
2923040b043STheodore Kilgore 		else
2933040b043STheodore Kilgore 			packet_type = INTER_PACKET;
2943040b043STheodore Kilgore 		gspca_frame_add(gspca_dev, packet_type,
295c3d86927SPatrice Chotard 				data, JEILINJ_MAX_TRANSFER);
296c3d86927SPatrice Chotard 	} else
297c3d86927SPatrice Chotard 		goto discard;
298c3d86927SPatrice Chotard 	return;
299c3d86927SPatrice Chotard discard:
300c3d86927SPatrice Chotard 	/* Discard data until a new frame starts. */
301c3d86927SPatrice Chotard 	gspca_dev->last_packet_type = DISCARD_PACKET;
3023040b043STheodore Kilgore }
3033040b043STheodore Kilgore 
3043040b043STheodore Kilgore /* This function is called at probe time just before sd_init */
3053040b043STheodore Kilgore static int sd_config(struct gspca_dev *gspca_dev,
3063040b043STheodore Kilgore 		const struct usb_device_id *id)
3073040b043STheodore Kilgore {
3083040b043STheodore Kilgore 	struct cam *cam = &gspca_dev->cam;
3093040b043STheodore Kilgore 	struct sd *dev  = (struct sd *) gspca_dev;
3103040b043STheodore Kilgore 
311713b466fSPatrice Chotard 	dev->type = id->driver_info;
3125396e62fSPatrice Chotard 	dev->quality = QUALITY_DEF;
3137d84a179SJean-François Moine 
3143040b043STheodore Kilgore 	cam->cam_mode = jlj_mode;
3156f8efcfbSPatrice Chotard 	cam->nmodes = ARRAY_SIZE(jlj_mode);
3163040b043STheodore Kilgore 	cam->bulk = 1;
317c3d86927SPatrice Chotard 	cam->bulk_nurbs = 1;
318c3d86927SPatrice Chotard 	cam->bulk_size = JEILINJ_MAX_TRANSFER;
3193040b043STheodore Kilgore 	return 0;
3203040b043STheodore Kilgore }
3213040b043STheodore Kilgore 
322c3d86927SPatrice Chotard static void sd_stopN(struct gspca_dev *gspca_dev)
3233040b043STheodore Kilgore {
324c3d86927SPatrice Chotard 	int i;
325c3d86927SPatrice Chotard 	u8 *buf;
3267d84a179SJean-François Moine 	static u8 stop_commands[][2] = {
327c3d86927SPatrice Chotard 		{0x71, 0x00},
328c3d86927SPatrice Chotard 		{0x70, 0x09},
329c3d86927SPatrice Chotard 		{0x71, 0x80},
330c3d86927SPatrice Chotard 		{0x70, 0x05}
331c3d86927SPatrice Chotard 	};
3323040b043STheodore Kilgore 
333c3d86927SPatrice Chotard 	for (;;) {
334c3d86927SPatrice Chotard 		/* get the image remaining blocks */
335c3d86927SPatrice Chotard 		usb_bulk_msg(gspca_dev->dev,
336c3d86927SPatrice Chotard 				gspca_dev->urb[0]->pipe,
337c3d86927SPatrice Chotard 				gspca_dev->urb[0]->transfer_buffer,
338c3d86927SPatrice Chotard 				JEILINJ_MAX_TRANSFER, NULL,
339c3d86927SPatrice Chotard 				JEILINJ_DATA_TIMEOUT);
340c3d86927SPatrice Chotard 
341c3d86927SPatrice Chotard 		/* search for 0xff 0xd9  (EOF for JPEG) */
342c3d86927SPatrice Chotard 		i = 0;
343c3d86927SPatrice Chotard 		buf = gspca_dev->urb[0]->transfer_buffer;
344c3d86927SPatrice Chotard 		while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
345c3d86927SPatrice Chotard 			((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
346c3d86927SPatrice Chotard 			i++;
347c3d86927SPatrice Chotard 
348c3d86927SPatrice Chotard 		if (i != (JEILINJ_MAX_TRANSFER - 1))
349c3d86927SPatrice Chotard 			/* last remaining block found */
350c3d86927SPatrice Chotard 			break;
351c3d86927SPatrice Chotard 		}
352c3d86927SPatrice Chotard 
353c3d86927SPatrice Chotard 	for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
354c3d86927SPatrice Chotard 		jlj_write2(gspca_dev, stop_commands[i]);
3553040b043STheodore Kilgore }
3563040b043STheodore Kilgore 
3573040b043STheodore Kilgore /* this function is called at probe and resume time */
3583040b043STheodore Kilgore static int sd_init(struct gspca_dev *gspca_dev)
3593040b043STheodore Kilgore {
3608715b16eSPatrice Chotard 	return gspca_dev->usb_err;
3613040b043STheodore Kilgore }
3623040b043STheodore Kilgore 
3633040b043STheodore Kilgore /* Set up for getting frames. */
3643040b043STheodore Kilgore static int sd_start(struct gspca_dev *gspca_dev)
3653040b043STheodore Kilgore {
3663040b043STheodore Kilgore 	struct sd *dev = (struct sd *) gspca_dev;
3673040b043STheodore Kilgore 
3683040b043STheodore Kilgore 	/* create the JPEG header */
3691966bc2aSOndrej Zary 	jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height,
3701966bc2aSOndrej Zary 			gspca_dev->pixfmt.width,
3713040b043STheodore Kilgore 			0x21);          /* JPEG 422 */
3723040b043STheodore Kilgore 	jpeg_set_qual(dev->jpeg_hdr, dev->quality);
37337d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Start streaming at %dx%d\n",
3741966bc2aSOndrej Zary 		  gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
3758715b16eSPatrice Chotard 	jlj_start(gspca_dev);
3768715b16eSPatrice Chotard 	return gspca_dev->usb_err;
3773040b043STheodore Kilgore }
3783040b043STheodore Kilgore 
3793040b043STheodore Kilgore /* Table of supported USB devices */
38095c967c1SJean-François Moine static const struct usb_device_id device_table[] = {
381713b466fSPatrice Chotard 	{USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
382713b466fSPatrice Chotard 	{USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
3833040b043STheodore Kilgore 	{}
3843040b043STheodore Kilgore };
3853040b043STheodore Kilgore 
3863040b043STheodore Kilgore MODULE_DEVICE_TABLE(usb, device_table);
3873040b043STheodore Kilgore 
388bfaab899SHans Verkuil static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
3895396e62fSPatrice Chotard {
390bfaab899SHans Verkuil 	struct gspca_dev *gspca_dev =
391bfaab899SHans Verkuil 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
392bfaab899SHans Verkuil 	struct sd *sd = (struct sd *)gspca_dev;
393bfaab899SHans Verkuil 
394bfaab899SHans Verkuil 	gspca_dev->usb_err = 0;
395bfaab899SHans Verkuil 
396bfaab899SHans Verkuil 	if (!gspca_dev->streaming)
397bfaab899SHans Verkuil 		return 0;
398bfaab899SHans Verkuil 
399bfaab899SHans Verkuil 	switch (ctrl->id) {
4005396e62fSPatrice Chotard 	case V4L2_CID_POWER_LINE_FREQUENCY:
401bfaab899SHans Verkuil 		setfreq(gspca_dev, ctrl->val);
402bfaab899SHans Verkuil 		break;
403bfaab899SHans Verkuil 	case V4L2_CID_RED_BALANCE:
404bfaab899SHans Verkuil 		setred(gspca_dev, ctrl->val);
405bfaab899SHans Verkuil 		break;
406bfaab899SHans Verkuil 	case V4L2_CID_GAIN:
407bfaab899SHans Verkuil 		setgreen(gspca_dev, ctrl->val);
408bfaab899SHans Verkuil 		break;
409bfaab899SHans Verkuil 	case V4L2_CID_BLUE_BALANCE:
410bfaab899SHans Verkuil 		setblue(gspca_dev, ctrl->val);
411bfaab899SHans Verkuil 		break;
412bfaab899SHans Verkuil 	case V4L2_CID_AUTOGAIN:
413bfaab899SHans Verkuil 		setautogain(gspca_dev, ctrl->val);
414bfaab899SHans Verkuil 		break;
415bfaab899SHans Verkuil 	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
416bfaab899SHans Verkuil 		jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
417bfaab899SHans Verkuil 		setcamquality(gspca_dev, ctrl->val);
4185396e62fSPatrice Chotard 		break;
4195396e62fSPatrice Chotard 	}
420bfaab899SHans Verkuil 	return gspca_dev->usb_err;
421bfaab899SHans Verkuil }
422bfaab899SHans Verkuil 
423bfaab899SHans Verkuil static const struct v4l2_ctrl_ops sd_ctrl_ops = {
424bfaab899SHans Verkuil 	.s_ctrl = sd_s_ctrl,
425bfaab899SHans Verkuil };
426bfaab899SHans Verkuil 
427bfaab899SHans Verkuil static int sd_init_controls(struct gspca_dev *gspca_dev)
428bfaab899SHans Verkuil {
429bfaab899SHans Verkuil 	struct sd *sd = (struct sd *)gspca_dev;
430bfaab899SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
431bfaab899SHans Verkuil 	static const struct v4l2_ctrl_config custom_autogain = {
432bfaab899SHans Verkuil 		.ops = &sd_ctrl_ops,
433bfaab899SHans Verkuil 		.id = V4L2_CID_AUTOGAIN,
434bfaab899SHans Verkuil 		.type = V4L2_CTRL_TYPE_INTEGER,
435bfaab899SHans Verkuil 		.name = "Automatic Gain (and Exposure)",
436bfaab899SHans Verkuil 		.max = 3,
437bfaab899SHans Verkuil 		.step = 1,
438bfaab899SHans Verkuil 		.def = 0,
439bfaab899SHans Verkuil 	};
440bfaab899SHans Verkuil 
441bfaab899SHans Verkuil 	gspca_dev->vdev.ctrl_handler = hdl;
442bfaab899SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 6);
443bfaab899SHans Verkuil 	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
444bfaab899SHans Verkuil 			V4L2_CID_POWER_LINE_FREQUENCY,
445bfaab899SHans Verkuil 			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
446bfaab899SHans Verkuil 			V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
447bfaab899SHans Verkuil 	v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
448bfaab899SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
449bfaab899SHans Verkuil 			V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
450bfaab899SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
451bfaab899SHans Verkuil 			V4L2_CID_GAIN, 0, 3, 1, 2);
452bfaab899SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
453bfaab899SHans Verkuil 			V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
454bfaab899SHans Verkuil 	sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
455bfaab899SHans Verkuil 			V4L2_CID_JPEG_COMPRESSION_QUALITY,
456bfaab899SHans Verkuil 			QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
457bfaab899SHans Verkuil 
458bfaab899SHans Verkuil 	if (hdl->error) {
459bfaab899SHans Verkuil 		pr_err("Could not initialize controls\n");
460bfaab899SHans Verkuil 		return hdl->error;
461bfaab899SHans Verkuil 	}
462bfaab899SHans Verkuil 	return 0;
4635396e62fSPatrice Chotard }
4645396e62fSPatrice Chotard 
4655396e62fSPatrice Chotard static int sd_set_jcomp(struct gspca_dev *gspca_dev,
466d88aab53SHans Verkuil 			const struct v4l2_jpegcompression *jcomp)
4675396e62fSPatrice Chotard {
4685396e62fSPatrice Chotard 	struct sd *sd = (struct sd *) gspca_dev;
4695396e62fSPatrice Chotard 
470bfaab899SHans Verkuil 	v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
4715396e62fSPatrice Chotard 	return 0;
4725396e62fSPatrice Chotard }
4735396e62fSPatrice Chotard 
4745396e62fSPatrice Chotard static int sd_get_jcomp(struct gspca_dev *gspca_dev,
4755396e62fSPatrice Chotard 			struct v4l2_jpegcompression *jcomp)
4765396e62fSPatrice Chotard {
4775396e62fSPatrice Chotard 	struct sd *sd = (struct sd *) gspca_dev;
4785396e62fSPatrice Chotard 
4795396e62fSPatrice Chotard 	memset(jcomp, 0, sizeof *jcomp);
480bfaab899SHans Verkuil 	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
4815396e62fSPatrice Chotard 	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
4825396e62fSPatrice Chotard 			| V4L2_JPEG_MARKER_DQT;
4835396e62fSPatrice Chotard 	return 0;
4845396e62fSPatrice Chotard }
4855396e62fSPatrice Chotard 
4865396e62fSPatrice Chotard 
4873040b043STheodore Kilgore /* sub-driver description */
488713b466fSPatrice Chotard static const struct sd_desc sd_desc_sakar_57379 = {
4893040b043STheodore Kilgore 	.name   = MODULE_NAME,
4903040b043STheodore Kilgore 	.config = sd_config,
4913040b043STheodore Kilgore 	.init   = sd_init,
4923040b043STheodore Kilgore 	.start  = sd_start,
493c3d86927SPatrice Chotard 	.stopN  = sd_stopN,
494c3d86927SPatrice Chotard 	.pkt_scan = sd_pkt_scan,
4953040b043STheodore Kilgore };
4963040b043STheodore Kilgore 
497713b466fSPatrice Chotard /* sub-driver description */
498713b466fSPatrice Chotard static const struct sd_desc sd_desc_sportscam_dv15 = {
499713b466fSPatrice Chotard 	.name   = MODULE_NAME,
500713b466fSPatrice Chotard 	.config = sd_config,
501713b466fSPatrice Chotard 	.init   = sd_init,
502bfaab899SHans Verkuil 	.init_controls = sd_init_controls,
503713b466fSPatrice Chotard 	.start  = sd_start,
504713b466fSPatrice Chotard 	.stopN  = sd_stopN,
505713b466fSPatrice Chotard 	.pkt_scan = sd_pkt_scan,
5065396e62fSPatrice Chotard 	.get_jcomp = sd_get_jcomp,
5075396e62fSPatrice Chotard 	.set_jcomp = sd_set_jcomp,
508713b466fSPatrice Chotard };
509713b466fSPatrice Chotard 
510713b466fSPatrice Chotard static const struct sd_desc *sd_desc[2] = {
511713b466fSPatrice Chotard 	&sd_desc_sakar_57379,
512713b466fSPatrice Chotard 	&sd_desc_sportscam_dv15
513713b466fSPatrice Chotard };
514713b466fSPatrice Chotard 
5153040b043STheodore Kilgore /* -- device connect -- */
5163040b043STheodore Kilgore static int sd_probe(struct usb_interface *intf,
5173040b043STheodore Kilgore 		const struct usb_device_id *id)
5183040b043STheodore Kilgore {
5193040b043STheodore Kilgore 	return gspca_dev_probe(intf, id,
520713b466fSPatrice Chotard 			sd_desc[id->driver_info],
5213040b043STheodore Kilgore 			sizeof(struct sd),
5223040b043STheodore Kilgore 			THIS_MODULE);
5233040b043STheodore Kilgore }
5243040b043STheodore Kilgore 
5253040b043STheodore Kilgore static struct usb_driver sd_driver = {
5263040b043STheodore Kilgore 	.name       = MODULE_NAME,
5273040b043STheodore Kilgore 	.id_table   = device_table,
5283040b043STheodore Kilgore 	.probe      = sd_probe,
5293040b043STheodore Kilgore 	.disconnect = gspca_disconnect,
5303040b043STheodore Kilgore #ifdef CONFIG_PM
5313040b043STheodore Kilgore 	.suspend = gspca_suspend,
5323040b043STheodore Kilgore 	.resume  = gspca_resume,
5338bb58964SHans de Goede 	.reset_resume = gspca_resume,
5343040b043STheodore Kilgore #endif
5353040b043STheodore Kilgore };
5363040b043STheodore Kilgore 
537ecb3b2b3SGreg Kroah-Hartman module_usb_driver(sd_driver);
538