xref: /openbmc/linux/drivers/media/usb/gspca/jeilinj.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * Jeilinj subdriver
40c0d06caSMauro Carvalho Chehab  *
50c0d06caSMauro Carvalho Chehab  * Supports some Jeilin dual-mode cameras which use bulk transport and
60c0d06caSMauro Carvalho Chehab  * download raw JPEG data.
70c0d06caSMauro Carvalho Chehab  *
80c0d06caSMauro Carvalho Chehab  * Copyright (C) 2009 Theodore Kilgore
90c0d06caSMauro Carvalho Chehab  *
100c0d06caSMauro Carvalho Chehab  * Sportscam DV15 support and control settings are
110c0d06caSMauro Carvalho Chehab  * Copyright (C) 2011 Patrice Chotard
120c0d06caSMauro Carvalho Chehab  */
130c0d06caSMauro Carvalho Chehab 
140c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
150c0d06caSMauro Carvalho Chehab 
160c0d06caSMauro Carvalho Chehab #define MODULE_NAME "jeilinj"
170c0d06caSMauro Carvalho Chehab 
180c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
190c0d06caSMauro Carvalho Chehab #include "gspca.h"
200c0d06caSMauro Carvalho Chehab #include "jpeg.h"
210c0d06caSMauro Carvalho Chehab 
220c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
230c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
240c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
250c0d06caSMauro Carvalho Chehab 
260c0d06caSMauro Carvalho Chehab /* Default timeouts, in ms */
270c0d06caSMauro Carvalho Chehab #define JEILINJ_CMD_TIMEOUT 500
280c0d06caSMauro Carvalho Chehab #define JEILINJ_CMD_DELAY 160
290c0d06caSMauro Carvalho Chehab #define JEILINJ_DATA_TIMEOUT 1000
300c0d06caSMauro Carvalho Chehab 
310c0d06caSMauro Carvalho Chehab /* Maximum transfer size to use. */
320c0d06caSMauro Carvalho Chehab #define JEILINJ_MAX_TRANSFER 0x200
330c0d06caSMauro Carvalho Chehab #define FRAME_HEADER_LEN 0x10
340c0d06caSMauro Carvalho Chehab #define FRAME_START 0xFFFFFFFF
350c0d06caSMauro Carvalho Chehab 
360c0d06caSMauro Carvalho Chehab enum {
370c0d06caSMauro Carvalho Chehab 	SAKAR_57379,
380c0d06caSMauro Carvalho Chehab 	SPORTSCAM_DV15,
390c0d06caSMauro Carvalho Chehab };
400c0d06caSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab #define CAMQUALITY_MIN 0	/* highest cam quality */
420c0d06caSMauro Carvalho Chehab #define CAMQUALITY_MAX 97	/* lowest cam quality  */
430c0d06caSMauro Carvalho Chehab 
440c0d06caSMauro Carvalho Chehab /* Structure to hold all of our device specific stuff */
450c0d06caSMauro Carvalho Chehab struct sd {
460c0d06caSMauro Carvalho Chehab 	struct gspca_dev gspca_dev;	/* !! must be the first item */
470c0d06caSMauro Carvalho Chehab 	int blocks_left;
480c0d06caSMauro Carvalho Chehab 	const struct v4l2_pix_format *cap_mode;
490c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl *freq;
500c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl *jpegqual;
510c0d06caSMauro Carvalho Chehab 	/* Driver stuff */
520c0d06caSMauro Carvalho Chehab 	u8 type;
530c0d06caSMauro Carvalho Chehab 	u8 quality;				 /* image quality */
540c0d06caSMauro Carvalho Chehab #define QUALITY_MIN 35
550c0d06caSMauro Carvalho Chehab #define QUALITY_MAX 85
560c0d06caSMauro Carvalho Chehab #define QUALITY_DEF 85
570c0d06caSMauro Carvalho Chehab 	u8 jpeg_hdr[JPEG_HDR_SZ];
580c0d06caSMauro Carvalho Chehab };
590c0d06caSMauro Carvalho Chehab 
600c0d06caSMauro Carvalho Chehab struct jlj_command {
610c0d06caSMauro Carvalho Chehab 	unsigned char instruction[2];
620c0d06caSMauro Carvalho Chehab 	unsigned char ack_wanted;
630c0d06caSMauro Carvalho Chehab 	unsigned char delay;
640c0d06caSMauro Carvalho Chehab };
650c0d06caSMauro Carvalho Chehab 
660c0d06caSMauro Carvalho Chehab /* AFAICT these cameras will only do 320x240. */
670c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format jlj_mode[] = {
680c0d06caSMauro Carvalho Chehab 	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
690c0d06caSMauro Carvalho Chehab 		.bytesperline = 320,
700c0d06caSMauro Carvalho Chehab 		.sizeimage = 320 * 240,
710c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
720c0d06caSMauro Carvalho Chehab 		.priv = 0},
730c0d06caSMauro Carvalho Chehab 	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
740c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
750c0d06caSMauro Carvalho Chehab 		.sizeimage = 640 * 480,
760c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_JPEG,
770c0d06caSMauro Carvalho Chehab 		.priv = 0}
780c0d06caSMauro Carvalho Chehab };
790c0d06caSMauro Carvalho Chehab 
800c0d06caSMauro Carvalho Chehab /*
810c0d06caSMauro Carvalho Chehab  * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
820c0d06caSMauro Carvalho Chehab  * and 0x82 for bulk transfer.
830c0d06caSMauro Carvalho Chehab  */
840c0d06caSMauro Carvalho Chehab 
850c0d06caSMauro Carvalho Chehab /* All commands are two bytes only */
jlj_write2(struct gspca_dev * gspca_dev,unsigned char * command)860c0d06caSMauro Carvalho Chehab static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
870c0d06caSMauro Carvalho Chehab {
880c0d06caSMauro Carvalho Chehab 	int retval;
890c0d06caSMauro Carvalho Chehab 
900c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
910c0d06caSMauro Carvalho Chehab 		return;
920c0d06caSMauro Carvalho Chehab 	memcpy(gspca_dev->usb_buf, command, 2);
930c0d06caSMauro Carvalho Chehab 	retval = usb_bulk_msg(gspca_dev->dev,
940c0d06caSMauro Carvalho Chehab 			usb_sndbulkpipe(gspca_dev->dev, 3),
950c0d06caSMauro Carvalho Chehab 			gspca_dev->usb_buf, 2, NULL, 500);
960c0d06caSMauro Carvalho Chehab 	if (retval < 0) {
970c0d06caSMauro Carvalho Chehab 		pr_err("command write [%02x] error %d\n",
980c0d06caSMauro Carvalho Chehab 		       gspca_dev->usb_buf[0], retval);
990c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = retval;
1000c0d06caSMauro Carvalho Chehab 	}
1010c0d06caSMauro Carvalho Chehab }
1020c0d06caSMauro Carvalho Chehab 
1030c0d06caSMauro Carvalho Chehab /* Responses are one byte only */
jlj_read1(struct gspca_dev * gspca_dev,unsigned char * response)1049825f376SMauro Carvalho Chehab static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response)
1050c0d06caSMauro Carvalho Chehab {
1060c0d06caSMauro Carvalho Chehab 	int retval;
1070c0d06caSMauro Carvalho Chehab 
1080c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
1090c0d06caSMauro Carvalho Chehab 		return;
1100c0d06caSMauro Carvalho Chehab 	retval = usb_bulk_msg(gspca_dev->dev,
1110c0d06caSMauro Carvalho Chehab 	usb_rcvbulkpipe(gspca_dev->dev, 0x84),
1120c0d06caSMauro Carvalho Chehab 				gspca_dev->usb_buf, 1, NULL, 500);
1139825f376SMauro Carvalho Chehab 	*response = gspca_dev->usb_buf[0];
1140c0d06caSMauro Carvalho Chehab 	if (retval < 0) {
1150c0d06caSMauro Carvalho Chehab 		pr_err("read command [%02x] error %d\n",
1160c0d06caSMauro Carvalho Chehab 		       gspca_dev->usb_buf[0], retval);
1170c0d06caSMauro Carvalho Chehab 		gspca_dev->usb_err = retval;
1180c0d06caSMauro Carvalho Chehab 	}
1190c0d06caSMauro Carvalho Chehab }
1200c0d06caSMauro Carvalho Chehab 
setfreq(struct gspca_dev * gspca_dev,s32 val)1210c0d06caSMauro Carvalho Chehab static void setfreq(struct gspca_dev *gspca_dev, s32 val)
1220c0d06caSMauro Carvalho Chehab {
1230c0d06caSMauro Carvalho Chehab 	u8 freq_commands[][2] = {
1240c0d06caSMauro Carvalho Chehab 		{0x71, 0x80},
1250c0d06caSMauro Carvalho Chehab 		{0x70, 0x07}
1260c0d06caSMauro Carvalho Chehab 	};
1270c0d06caSMauro Carvalho Chehab 
1280c0d06caSMauro Carvalho Chehab 	freq_commands[0][1] |= val >> 1;
1290c0d06caSMauro Carvalho Chehab 
1300c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, freq_commands[0]);
1310c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, freq_commands[1]);
1320c0d06caSMauro Carvalho Chehab }
1330c0d06caSMauro Carvalho Chehab 
setcamquality(struct gspca_dev * gspca_dev,s32 val)1340c0d06caSMauro Carvalho Chehab static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
1350c0d06caSMauro Carvalho Chehab {
1360c0d06caSMauro Carvalho Chehab 	u8 quality_commands[][2] = {
1370c0d06caSMauro Carvalho Chehab 		{0x71, 0x1E},
1380c0d06caSMauro Carvalho Chehab 		{0x70, 0x06}
1390c0d06caSMauro Carvalho Chehab 	};
1400c0d06caSMauro Carvalho Chehab 	u8 camquality;
1410c0d06caSMauro Carvalho Chehab 
1420c0d06caSMauro Carvalho Chehab 	/* adapt camera quality from jpeg quality */
1430c0d06caSMauro Carvalho Chehab 	camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
1440c0d06caSMauro Carvalho Chehab 		/ (QUALITY_MAX - QUALITY_MIN);
1450c0d06caSMauro Carvalho Chehab 	quality_commands[0][1] += camquality;
1460c0d06caSMauro Carvalho Chehab 
1470c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, quality_commands[0]);
1480c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, quality_commands[1]);
1490c0d06caSMauro Carvalho Chehab }
1500c0d06caSMauro Carvalho Chehab 
setautogain(struct gspca_dev * gspca_dev,s32 val)1510c0d06caSMauro Carvalho Chehab static void setautogain(struct gspca_dev *gspca_dev, s32 val)
1520c0d06caSMauro Carvalho Chehab {
1530c0d06caSMauro Carvalho Chehab 	u8 autogain_commands[][2] = {
1540c0d06caSMauro Carvalho Chehab 		{0x94, 0x02},
1550c0d06caSMauro Carvalho Chehab 		{0xcf, 0x00}
1560c0d06caSMauro Carvalho Chehab 	};
1570c0d06caSMauro Carvalho Chehab 
1580c0d06caSMauro Carvalho Chehab 	autogain_commands[1][1] = val << 4;
1590c0d06caSMauro Carvalho Chehab 
1600c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, autogain_commands[0]);
1610c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, autogain_commands[1]);
1620c0d06caSMauro Carvalho Chehab }
1630c0d06caSMauro Carvalho Chehab 
setred(struct gspca_dev * gspca_dev,s32 val)1640c0d06caSMauro Carvalho Chehab static void setred(struct gspca_dev *gspca_dev, s32 val)
1650c0d06caSMauro Carvalho Chehab {
1660c0d06caSMauro Carvalho Chehab 	u8 setred_commands[][2] = {
1670c0d06caSMauro Carvalho Chehab 		{0x94, 0x02},
1680c0d06caSMauro Carvalho Chehab 		{0xe6, 0x00}
1690c0d06caSMauro Carvalho Chehab 	};
1700c0d06caSMauro Carvalho Chehab 
1710c0d06caSMauro Carvalho Chehab 	setred_commands[1][1] = val;
1720c0d06caSMauro Carvalho Chehab 
1730c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, setred_commands[0]);
1740c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, setred_commands[1]);
1750c0d06caSMauro Carvalho Chehab }
1760c0d06caSMauro Carvalho Chehab 
setgreen(struct gspca_dev * gspca_dev,s32 val)1770c0d06caSMauro Carvalho Chehab static void setgreen(struct gspca_dev *gspca_dev, s32 val)
1780c0d06caSMauro Carvalho Chehab {
1790c0d06caSMauro Carvalho Chehab 	u8 setgreen_commands[][2] = {
1800c0d06caSMauro Carvalho Chehab 		{0x94, 0x02},
1810c0d06caSMauro Carvalho Chehab 		{0xe7, 0x00}
1820c0d06caSMauro Carvalho Chehab 	};
1830c0d06caSMauro Carvalho Chehab 
1840c0d06caSMauro Carvalho Chehab 	setgreen_commands[1][1] = val;
1850c0d06caSMauro Carvalho Chehab 
1860c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, setgreen_commands[0]);
1870c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, setgreen_commands[1]);
1880c0d06caSMauro Carvalho Chehab }
1890c0d06caSMauro Carvalho Chehab 
setblue(struct gspca_dev * gspca_dev,s32 val)1900c0d06caSMauro Carvalho Chehab static void setblue(struct gspca_dev *gspca_dev, s32 val)
1910c0d06caSMauro Carvalho Chehab {
1920c0d06caSMauro Carvalho Chehab 	u8 setblue_commands[][2] = {
1930c0d06caSMauro Carvalho Chehab 		{0x94, 0x02},
1940c0d06caSMauro Carvalho Chehab 		{0xe9, 0x00}
1950c0d06caSMauro Carvalho Chehab 	};
1960c0d06caSMauro Carvalho Chehab 
1970c0d06caSMauro Carvalho Chehab 	setblue_commands[1][1] = val;
1980c0d06caSMauro Carvalho Chehab 
1990c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, setblue_commands[0]);
2000c0d06caSMauro Carvalho Chehab 	jlj_write2(gspca_dev, setblue_commands[1]);
2010c0d06caSMauro Carvalho Chehab }
2020c0d06caSMauro Carvalho Chehab 
jlj_start(struct gspca_dev * gspca_dev)2030c0d06caSMauro Carvalho Chehab static int jlj_start(struct gspca_dev *gspca_dev)
2040c0d06caSMauro Carvalho Chehab {
2050c0d06caSMauro Carvalho Chehab 	int i;
2060c0d06caSMauro Carvalho Chehab 	int start_commands_size;
2070c0d06caSMauro Carvalho Chehab 	u8 response = 0xff;
2080c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
2090c0d06caSMauro Carvalho Chehab 	struct jlj_command start_commands[] = {
2100c0d06caSMauro Carvalho Chehab 		{{0x71, 0x81}, 0, 0},
2110c0d06caSMauro Carvalho Chehab 		{{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
2120c0d06caSMauro Carvalho Chehab 		{{0x95, 0x70}, 1, 0},
2130c0d06caSMauro Carvalho Chehab 		{{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
2140c0d06caSMauro Carvalho Chehab 		{{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
2150c0d06caSMauro Carvalho Chehab 		{{0x95, 0x70}, 1, 0},
2160c0d06caSMauro Carvalho Chehab 		{{0x71, 0x00}, 0, 0},   /* start streaming ??*/
2170c0d06caSMauro Carvalho Chehab 		{{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
2180c0d06caSMauro Carvalho Chehab 		{{0x95, 0x70}, 1, 0},
2190c0d06caSMauro Carvalho Chehab #define SPORTSCAM_DV15_CMD_SIZE 9
2200c0d06caSMauro Carvalho Chehab 		{{0x94, 0x02}, 0, 0},
2210c0d06caSMauro Carvalho Chehab 		{{0xde, 0x24}, 0, 0},
2220c0d06caSMauro Carvalho Chehab 		{{0x94, 0x02}, 0, 0},
2230c0d06caSMauro Carvalho Chehab 		{{0xdd, 0xf0}, 0, 0},
2240c0d06caSMauro Carvalho Chehab 		{{0x94, 0x02}, 0, 0},
2250c0d06caSMauro Carvalho Chehab 		{{0xe3, 0x2c}, 0, 0},
2260c0d06caSMauro Carvalho Chehab 		{{0x94, 0x02}, 0, 0},
2270c0d06caSMauro Carvalho Chehab 		{{0xe4, 0x00}, 0, 0},
2280c0d06caSMauro Carvalho Chehab 		{{0x94, 0x02}, 0, 0},
2290c0d06caSMauro Carvalho Chehab 		{{0xe5, 0x00}, 0, 0},
2300c0d06caSMauro Carvalho Chehab 		{{0x94, 0x02}, 0, 0},
2310c0d06caSMauro Carvalho Chehab 		{{0xe6, 0x2c}, 0, 0},
2320c0d06caSMauro Carvalho Chehab 		{{0x94, 0x03}, 0, 0},
2330c0d06caSMauro Carvalho Chehab 		{{0xaa, 0x00}, 0, 0}
2340c0d06caSMauro Carvalho Chehab 	};
2350c0d06caSMauro Carvalho Chehab 
2360c0d06caSMauro Carvalho Chehab 	sd->blocks_left = 0;
2370c0d06caSMauro Carvalho Chehab 	/* Under Windows, USB spy shows that only the 9 first start
2380c0d06caSMauro Carvalho Chehab 	 * commands are used for SPORTSCAM_DV15 webcam
2390c0d06caSMauro Carvalho Chehab 	 */
2400c0d06caSMauro Carvalho Chehab 	if (sd->type == SPORTSCAM_DV15)
2410c0d06caSMauro Carvalho Chehab 		start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
2420c0d06caSMauro Carvalho Chehab 	else
2430c0d06caSMauro Carvalho Chehab 		start_commands_size = ARRAY_SIZE(start_commands);
2440c0d06caSMauro Carvalho Chehab 
2450c0d06caSMauro Carvalho Chehab 	for (i = 0; i < start_commands_size; i++) {
2460c0d06caSMauro Carvalho Chehab 		jlj_write2(gspca_dev, start_commands[i].instruction);
2470c0d06caSMauro Carvalho Chehab 		if (start_commands[i].delay)
2480c0d06caSMauro Carvalho Chehab 			msleep(start_commands[i].delay);
2490c0d06caSMauro Carvalho Chehab 		if (start_commands[i].ack_wanted)
2509825f376SMauro Carvalho Chehab 			jlj_read1(gspca_dev, &response);
2510c0d06caSMauro Carvalho Chehab 	}
2520c0d06caSMauro Carvalho Chehab 	setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
2530c0d06caSMauro Carvalho Chehab 	msleep(2);
2540c0d06caSMauro Carvalho Chehab 	setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
2550c0d06caSMauro Carvalho Chehab 	if (gspca_dev->usb_err < 0)
25652173c5fSJoe Perches 		gspca_err(gspca_dev, "Start streaming command failed\n");
2570c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
2580c0d06caSMauro Carvalho Chehab }
2590c0d06caSMauro Carvalho Chehab 
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)2600c0d06caSMauro Carvalho Chehab static void sd_pkt_scan(struct gspca_dev *gspca_dev,
2610c0d06caSMauro Carvalho Chehab 			u8 *data, int len)
2620c0d06caSMauro Carvalho Chehab {
2630c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
2640c0d06caSMauro Carvalho Chehab 	int packet_type;
2650c0d06caSMauro Carvalho Chehab 	u32 header_marker;
2660c0d06caSMauro Carvalho Chehab 
26737d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_STREAM, "Got %d bytes out of %d for Block 0\n",
2680c0d06caSMauro Carvalho Chehab 		  len, JEILINJ_MAX_TRANSFER);
2690c0d06caSMauro Carvalho Chehab 	if (len != JEILINJ_MAX_TRANSFER) {
27037d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_PACK, "bad length\n");
2710c0d06caSMauro Carvalho Chehab 		goto discard;
2720c0d06caSMauro Carvalho Chehab 	}
2730c0d06caSMauro Carvalho Chehab 	/* check if it's start of frame */
2740c0d06caSMauro Carvalho Chehab 	header_marker = ((u32 *)data)[0];
2750c0d06caSMauro Carvalho Chehab 	if (header_marker == FRAME_START) {
2760c0d06caSMauro Carvalho Chehab 		sd->blocks_left = data[0x0a] - 1;
27737d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "blocks_left = 0x%x\n",
27837d5efb0SJoe Perches 			  sd->blocks_left);
2790c0d06caSMauro Carvalho Chehab 		/* Start a new frame, and add the JPEG header, first thing */
2800c0d06caSMauro Carvalho Chehab 		gspca_frame_add(gspca_dev, FIRST_PACKET,
2810c0d06caSMauro Carvalho Chehab 				sd->jpeg_hdr, JPEG_HDR_SZ);
2820c0d06caSMauro Carvalho Chehab 		/* Toss line 0 of data block 0, keep the rest. */
2830c0d06caSMauro Carvalho Chehab 		gspca_frame_add(gspca_dev, INTER_PACKET,
2840c0d06caSMauro Carvalho Chehab 				data + FRAME_HEADER_LEN,
2850c0d06caSMauro Carvalho Chehab 				JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
2860c0d06caSMauro Carvalho Chehab 	} else if (sd->blocks_left > 0) {
28737d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "%d blocks remaining for frame\n",
2880c0d06caSMauro Carvalho Chehab 			  sd->blocks_left);
2890c0d06caSMauro Carvalho Chehab 		sd->blocks_left -= 1;
2900c0d06caSMauro Carvalho Chehab 		if (sd->blocks_left == 0)
2910c0d06caSMauro Carvalho Chehab 			packet_type = LAST_PACKET;
2920c0d06caSMauro Carvalho Chehab 		else
2930c0d06caSMauro Carvalho Chehab 			packet_type = INTER_PACKET;
2940c0d06caSMauro Carvalho Chehab 		gspca_frame_add(gspca_dev, packet_type,
2950c0d06caSMauro Carvalho Chehab 				data, JEILINJ_MAX_TRANSFER);
2960c0d06caSMauro Carvalho Chehab 	} else
2970c0d06caSMauro Carvalho Chehab 		goto discard;
2980c0d06caSMauro Carvalho Chehab 	return;
2990c0d06caSMauro Carvalho Chehab discard:
3000c0d06caSMauro Carvalho Chehab 	/* Discard data until a new frame starts. */
3010c0d06caSMauro Carvalho Chehab 	gspca_dev->last_packet_type = DISCARD_PACKET;
3020c0d06caSMauro Carvalho Chehab }
3030c0d06caSMauro Carvalho Chehab 
3040c0d06caSMauro Carvalho Chehab /* This function is called at probe time just before sd_init */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)3050c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev,
3060c0d06caSMauro Carvalho Chehab 		const struct usb_device_id *id)
3070c0d06caSMauro Carvalho Chehab {
3080c0d06caSMauro Carvalho Chehab 	struct cam *cam = &gspca_dev->cam;
3090c0d06caSMauro Carvalho Chehab 	struct sd *dev  = (struct sd *) gspca_dev;
3100c0d06caSMauro Carvalho Chehab 
3110c0d06caSMauro Carvalho Chehab 	dev->type = id->driver_info;
3120c0d06caSMauro Carvalho Chehab 	dev->quality = QUALITY_DEF;
3130c0d06caSMauro Carvalho Chehab 
3140c0d06caSMauro Carvalho Chehab 	cam->cam_mode = jlj_mode;
3150c0d06caSMauro Carvalho Chehab 	cam->nmodes = ARRAY_SIZE(jlj_mode);
3160c0d06caSMauro Carvalho Chehab 	cam->bulk = 1;
3170c0d06caSMauro Carvalho Chehab 	cam->bulk_nurbs = 1;
3180c0d06caSMauro Carvalho Chehab 	cam->bulk_size = JEILINJ_MAX_TRANSFER;
3190c0d06caSMauro Carvalho Chehab 	return 0;
3200c0d06caSMauro Carvalho Chehab }
3210c0d06caSMauro Carvalho Chehab 
sd_stopN(struct gspca_dev * gspca_dev)3220c0d06caSMauro Carvalho Chehab static void sd_stopN(struct gspca_dev *gspca_dev)
3230c0d06caSMauro Carvalho Chehab {
3240c0d06caSMauro Carvalho Chehab 	int i;
3250c0d06caSMauro Carvalho Chehab 	u8 *buf;
3260c0d06caSMauro Carvalho Chehab 	static u8 stop_commands[][2] = {
3270c0d06caSMauro Carvalho Chehab 		{0x71, 0x00},
3280c0d06caSMauro Carvalho Chehab 		{0x70, 0x09},
3290c0d06caSMauro Carvalho Chehab 		{0x71, 0x80},
3300c0d06caSMauro Carvalho Chehab 		{0x70, 0x05}
3310c0d06caSMauro Carvalho Chehab 	};
3320c0d06caSMauro Carvalho Chehab 
3330c0d06caSMauro Carvalho Chehab 	for (;;) {
3340c0d06caSMauro Carvalho Chehab 		/* get the image remaining blocks */
3350c0d06caSMauro Carvalho Chehab 		usb_bulk_msg(gspca_dev->dev,
3360c0d06caSMauro Carvalho Chehab 				gspca_dev->urb[0]->pipe,
3370c0d06caSMauro Carvalho Chehab 				gspca_dev->urb[0]->transfer_buffer,
3380c0d06caSMauro Carvalho Chehab 				JEILINJ_MAX_TRANSFER, NULL,
3390c0d06caSMauro Carvalho Chehab 				JEILINJ_DATA_TIMEOUT);
3400c0d06caSMauro Carvalho Chehab 
3410c0d06caSMauro Carvalho Chehab 		/* search for 0xff 0xd9  (EOF for JPEG) */
3420c0d06caSMauro Carvalho Chehab 		i = 0;
3430c0d06caSMauro Carvalho Chehab 		buf = gspca_dev->urb[0]->transfer_buffer;
3440c0d06caSMauro Carvalho Chehab 		while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
3450c0d06caSMauro Carvalho Chehab 			((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
3460c0d06caSMauro Carvalho Chehab 			i++;
3470c0d06caSMauro Carvalho Chehab 
3480c0d06caSMauro Carvalho Chehab 		if (i != (JEILINJ_MAX_TRANSFER - 1))
3490c0d06caSMauro Carvalho Chehab 			/* last remaining block found */
3500c0d06caSMauro Carvalho Chehab 			break;
3510c0d06caSMauro Carvalho Chehab 		}
3520c0d06caSMauro Carvalho Chehab 
3530c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
3540c0d06caSMauro Carvalho Chehab 		jlj_write2(gspca_dev, stop_commands[i]);
3550c0d06caSMauro Carvalho Chehab }
3560c0d06caSMauro Carvalho Chehab 
3570c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)3580c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev)
3590c0d06caSMauro Carvalho Chehab {
3600c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
3610c0d06caSMauro Carvalho Chehab }
3620c0d06caSMauro Carvalho Chehab 
3630c0d06caSMauro Carvalho Chehab /* Set up for getting frames. */
sd_start(struct gspca_dev * gspca_dev)3640c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev)
3650c0d06caSMauro Carvalho Chehab {
3660c0d06caSMauro Carvalho Chehab 	struct sd *dev = (struct sd *) gspca_dev;
3670c0d06caSMauro Carvalho Chehab 
3680c0d06caSMauro Carvalho Chehab 	/* create the JPEG header */
3691966bc2aSOndrej Zary 	jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height,
3701966bc2aSOndrej Zary 			gspca_dev->pixfmt.width,
3710c0d06caSMauro Carvalho Chehab 			0x21);          /* JPEG 422 */
3720c0d06caSMauro Carvalho Chehab 	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);
3750c0d06caSMauro Carvalho Chehab 	jlj_start(gspca_dev);
3760c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
3770c0d06caSMauro Carvalho Chehab }
3780c0d06caSMauro Carvalho Chehab 
3790c0d06caSMauro Carvalho Chehab /* Table of supported USB devices */
3800c0d06caSMauro Carvalho Chehab static const struct usb_device_id device_table[] = {
3810c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
3820c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
3830c0d06caSMauro Carvalho Chehab 	{}
3840c0d06caSMauro Carvalho Chehab };
3850c0d06caSMauro Carvalho Chehab 
3860c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table);
3870c0d06caSMauro Carvalho Chehab 
sd_s_ctrl(struct v4l2_ctrl * ctrl)3880c0d06caSMauro Carvalho Chehab static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
3890c0d06caSMauro Carvalho Chehab {
3900c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev =
3910c0d06caSMauro Carvalho Chehab 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
3920c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
3930c0d06caSMauro Carvalho Chehab 
3940c0d06caSMauro Carvalho Chehab 	gspca_dev->usb_err = 0;
3950c0d06caSMauro Carvalho Chehab 
3960c0d06caSMauro Carvalho Chehab 	if (!gspca_dev->streaming)
3970c0d06caSMauro Carvalho Chehab 		return 0;
3980c0d06caSMauro Carvalho Chehab 
3990c0d06caSMauro Carvalho Chehab 	switch (ctrl->id) {
4000c0d06caSMauro Carvalho Chehab 	case V4L2_CID_POWER_LINE_FREQUENCY:
4010c0d06caSMauro Carvalho Chehab 		setfreq(gspca_dev, ctrl->val);
4020c0d06caSMauro Carvalho Chehab 		break;
4030c0d06caSMauro Carvalho Chehab 	case V4L2_CID_RED_BALANCE:
4040c0d06caSMauro Carvalho Chehab 		setred(gspca_dev, ctrl->val);
4050c0d06caSMauro Carvalho Chehab 		break;
4060c0d06caSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
4070c0d06caSMauro Carvalho Chehab 		setgreen(gspca_dev, ctrl->val);
4080c0d06caSMauro Carvalho Chehab 		break;
4090c0d06caSMauro Carvalho Chehab 	case V4L2_CID_BLUE_BALANCE:
4100c0d06caSMauro Carvalho Chehab 		setblue(gspca_dev, ctrl->val);
4110c0d06caSMauro Carvalho Chehab 		break;
4120c0d06caSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
4130c0d06caSMauro Carvalho Chehab 		setautogain(gspca_dev, ctrl->val);
4140c0d06caSMauro Carvalho Chehab 		break;
4150c0d06caSMauro Carvalho Chehab 	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
4160c0d06caSMauro Carvalho Chehab 		jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
4170c0d06caSMauro Carvalho Chehab 		setcamquality(gspca_dev, ctrl->val);
4180c0d06caSMauro Carvalho Chehab 		break;
4190c0d06caSMauro Carvalho Chehab 	}
4200c0d06caSMauro Carvalho Chehab 	return gspca_dev->usb_err;
4210c0d06caSMauro Carvalho Chehab }
4220c0d06caSMauro Carvalho Chehab 
4230c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops sd_ctrl_ops = {
4240c0d06caSMauro Carvalho Chehab 	.s_ctrl = sd_s_ctrl,
4250c0d06caSMauro Carvalho Chehab };
4260c0d06caSMauro Carvalho Chehab 
sd_init_controls(struct gspca_dev * gspca_dev)4270c0d06caSMauro Carvalho Chehab static int sd_init_controls(struct gspca_dev *gspca_dev)
4280c0d06caSMauro Carvalho Chehab {
4290c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
4300c0d06caSMauro Carvalho Chehab 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
4310c0d06caSMauro Carvalho Chehab 	static const struct v4l2_ctrl_config custom_autogain = {
4320c0d06caSMauro Carvalho Chehab 		.ops = &sd_ctrl_ops,
4330c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_AUTOGAIN,
4340c0d06caSMauro Carvalho Chehab 		.type = V4L2_CTRL_TYPE_INTEGER,
4350c0d06caSMauro Carvalho Chehab 		.name = "Automatic Gain (and Exposure)",
4360c0d06caSMauro Carvalho Chehab 		.max = 3,
4370c0d06caSMauro Carvalho Chehab 		.step = 1,
4380c0d06caSMauro Carvalho Chehab 		.def = 0,
4390c0d06caSMauro Carvalho Chehab 	};
4400c0d06caSMauro Carvalho Chehab 
4410c0d06caSMauro Carvalho Chehab 	gspca_dev->vdev.ctrl_handler = hdl;
4420c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_handler_init(hdl, 6);
4430c0d06caSMauro Carvalho Chehab 	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
4440c0d06caSMauro Carvalho Chehab 			V4L2_CID_POWER_LINE_FREQUENCY,
4450c0d06caSMauro Carvalho Chehab 			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
4460c0d06caSMauro Carvalho Chehab 			V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
4470c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
4480c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4490c0d06caSMauro Carvalho Chehab 			V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
4500c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4510c0d06caSMauro Carvalho Chehab 			V4L2_CID_GAIN, 0, 3, 1, 2);
4520c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4530c0d06caSMauro Carvalho Chehab 			V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
4540c0d06caSMauro Carvalho Chehab 	sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
4550c0d06caSMauro Carvalho Chehab 			V4L2_CID_JPEG_COMPRESSION_QUALITY,
4560c0d06caSMauro Carvalho Chehab 			QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
4570c0d06caSMauro Carvalho Chehab 
4580c0d06caSMauro Carvalho Chehab 	if (hdl->error) {
4590c0d06caSMauro Carvalho Chehab 		pr_err("Could not initialize controls\n");
4600c0d06caSMauro Carvalho Chehab 		return hdl->error;
4610c0d06caSMauro Carvalho Chehab 	}
4620c0d06caSMauro Carvalho Chehab 	return 0;
4630c0d06caSMauro Carvalho Chehab }
4640c0d06caSMauro Carvalho Chehab 
sd_set_jcomp(struct gspca_dev * gspca_dev,const struct v4l2_jpegcompression * jcomp)4650c0d06caSMauro Carvalho Chehab static int sd_set_jcomp(struct gspca_dev *gspca_dev,
466d88aab53SHans Verkuil 			const struct v4l2_jpegcompression *jcomp)
4670c0d06caSMauro Carvalho Chehab {
4680c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4690c0d06caSMauro Carvalho Chehab 
4700c0d06caSMauro Carvalho Chehab 	v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
4710c0d06caSMauro Carvalho Chehab 	return 0;
4720c0d06caSMauro Carvalho Chehab }
4730c0d06caSMauro Carvalho Chehab 
sd_get_jcomp(struct gspca_dev * gspca_dev,struct v4l2_jpegcompression * jcomp)4740c0d06caSMauro Carvalho Chehab static int sd_get_jcomp(struct gspca_dev *gspca_dev,
4750c0d06caSMauro Carvalho Chehab 			struct v4l2_jpegcompression *jcomp)
4760c0d06caSMauro Carvalho Chehab {
4770c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4780c0d06caSMauro Carvalho Chehab 
4790c0d06caSMauro Carvalho Chehab 	memset(jcomp, 0, sizeof *jcomp);
4800c0d06caSMauro Carvalho Chehab 	jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
4810c0d06caSMauro Carvalho Chehab 	jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
4820c0d06caSMauro Carvalho Chehab 			| V4L2_JPEG_MARKER_DQT;
4830c0d06caSMauro Carvalho Chehab 	return 0;
4840c0d06caSMauro Carvalho Chehab }
4850c0d06caSMauro Carvalho Chehab 
4860c0d06caSMauro Carvalho Chehab 
4870c0d06caSMauro Carvalho Chehab /* sub-driver description */
4880c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc_sakar_57379 = {
4890c0d06caSMauro Carvalho Chehab 	.name   = MODULE_NAME,
4900c0d06caSMauro Carvalho Chehab 	.config = sd_config,
4910c0d06caSMauro Carvalho Chehab 	.init   = sd_init,
4920c0d06caSMauro Carvalho Chehab 	.start  = sd_start,
4930c0d06caSMauro Carvalho Chehab 	.stopN  = sd_stopN,
4940c0d06caSMauro Carvalho Chehab 	.pkt_scan = sd_pkt_scan,
4950c0d06caSMauro Carvalho Chehab };
4960c0d06caSMauro Carvalho Chehab 
4970c0d06caSMauro Carvalho Chehab /* sub-driver description */
4980c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc_sportscam_dv15 = {
4990c0d06caSMauro Carvalho Chehab 	.name   = MODULE_NAME,
5000c0d06caSMauro Carvalho Chehab 	.config = sd_config,
5010c0d06caSMauro Carvalho Chehab 	.init   = sd_init,
5020c0d06caSMauro Carvalho Chehab 	.init_controls = sd_init_controls,
5030c0d06caSMauro Carvalho Chehab 	.start  = sd_start,
5040c0d06caSMauro Carvalho Chehab 	.stopN  = sd_stopN,
5050c0d06caSMauro Carvalho Chehab 	.pkt_scan = sd_pkt_scan,
5060c0d06caSMauro Carvalho Chehab 	.get_jcomp = sd_get_jcomp,
5070c0d06caSMauro Carvalho Chehab 	.set_jcomp = sd_set_jcomp,
5080c0d06caSMauro Carvalho Chehab };
5090c0d06caSMauro Carvalho Chehab 
5100c0d06caSMauro Carvalho Chehab static const struct sd_desc *sd_desc[2] = {
5110c0d06caSMauro Carvalho Chehab 	&sd_desc_sakar_57379,
5120c0d06caSMauro Carvalho Chehab 	&sd_desc_sportscam_dv15
5130c0d06caSMauro Carvalho Chehab };
5140c0d06caSMauro Carvalho Chehab 
5150c0d06caSMauro Carvalho Chehab /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)5160c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf,
5170c0d06caSMauro Carvalho Chehab 		const struct usb_device_id *id)
5180c0d06caSMauro Carvalho Chehab {
5190c0d06caSMauro Carvalho Chehab 	return gspca_dev_probe(intf, id,
5200c0d06caSMauro Carvalho Chehab 			sd_desc[id->driver_info],
5210c0d06caSMauro Carvalho Chehab 			sizeof(struct sd),
5220c0d06caSMauro Carvalho Chehab 			THIS_MODULE);
5230c0d06caSMauro Carvalho Chehab }
5240c0d06caSMauro Carvalho Chehab 
5250c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = {
5260c0d06caSMauro Carvalho Chehab 	.name       = MODULE_NAME,
5270c0d06caSMauro Carvalho Chehab 	.id_table   = device_table,
5280c0d06caSMauro Carvalho Chehab 	.probe      = sd_probe,
5290c0d06caSMauro Carvalho Chehab 	.disconnect = gspca_disconnect,
5300c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
5310c0d06caSMauro Carvalho Chehab 	.suspend = gspca_suspend,
5320c0d06caSMauro Carvalho Chehab 	.resume  = gspca_resume,
5330c0d06caSMauro Carvalho Chehab 	.reset_resume = gspca_resume,
5340c0d06caSMauro Carvalho Chehab #endif
5350c0d06caSMauro Carvalho Chehab };
5360c0d06caSMauro Carvalho Chehab 
5370c0d06caSMauro Carvalho Chehab module_usb_driver(sd_driver);
538