xref: /openbmc/linux/drivers/media/usb/gspca/jl2005bcd.c (revision 844db450e6e2cf710752af1a019a877af390b541)
10c0d06caSMauro Carvalho Chehab /*
20c0d06caSMauro Carvalho Chehab  * Jeilin JL2005B/C/D library
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
50c0d06caSMauro Carvalho Chehab  *
60c0d06caSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
70c0d06caSMauro Carvalho Chehab  * it under the terms of the GNU General Public License as published by
80c0d06caSMauro Carvalho Chehab  * the Free Software Foundation; either version 2 of the License, or
90c0d06caSMauro Carvalho Chehab  * any later version.
100c0d06caSMauro Carvalho Chehab  *
110c0d06caSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
120c0d06caSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
130c0d06caSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
140c0d06caSMauro Carvalho Chehab  * GNU General Public License for more details.
150c0d06caSMauro Carvalho Chehab  *
160c0d06caSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
170c0d06caSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
180c0d06caSMauro Carvalho Chehab  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
190c0d06caSMauro Carvalho Chehab  */
200c0d06caSMauro Carvalho Chehab 
210c0d06caSMauro Carvalho Chehab #define MODULE_NAME "jl2005bcd"
220c0d06caSMauro Carvalho Chehab 
230c0d06caSMauro Carvalho Chehab #include <linux/workqueue.h>
240c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
250c0d06caSMauro Carvalho Chehab #include "gspca.h"
260c0d06caSMauro Carvalho Chehab 
270c0d06caSMauro Carvalho Chehab 
280c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
290c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
300c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
310c0d06caSMauro Carvalho Chehab 
320c0d06caSMauro Carvalho Chehab /* Default timeouts, in ms */
330c0d06caSMauro Carvalho Chehab #define JL2005C_CMD_TIMEOUT 500
340c0d06caSMauro Carvalho Chehab #define JL2005C_DATA_TIMEOUT 1000
350c0d06caSMauro Carvalho Chehab 
360c0d06caSMauro Carvalho Chehab /* Maximum transfer size to use. */
370c0d06caSMauro Carvalho Chehab #define JL2005C_MAX_TRANSFER 0x200
380c0d06caSMauro Carvalho Chehab #define FRAME_HEADER_LEN 16
390c0d06caSMauro Carvalho Chehab 
400c0d06caSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab /* specific webcam descriptor */
420c0d06caSMauro Carvalho Chehab struct sd {
430c0d06caSMauro Carvalho Chehab 	struct gspca_dev gspca_dev;  /* !! must be the first item */
440c0d06caSMauro Carvalho Chehab 	unsigned char firmware_id[6];
450c0d06caSMauro Carvalho Chehab 	const struct v4l2_pix_format *cap_mode;
460c0d06caSMauro Carvalho Chehab 	/* Driver stuff */
470c0d06caSMauro Carvalho Chehab 	struct work_struct work_struct;
480c0d06caSMauro Carvalho Chehab 	struct workqueue_struct *work_thread;
490c0d06caSMauro Carvalho Chehab 	u8 frame_brightness;
500c0d06caSMauro Carvalho Chehab 	int block_size;	/* block size of camera */
510c0d06caSMauro Carvalho Chehab 	int vga;	/* 1 if vga cam, 0 if cif cam */
520c0d06caSMauro Carvalho Chehab };
530c0d06caSMauro Carvalho Chehab 
540c0d06caSMauro Carvalho Chehab 
550c0d06caSMauro Carvalho Chehab /* Camera has two resolution settings. What they are depends on model. */
560c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format cif_mode[] = {
570c0d06caSMauro Carvalho Chehab 	{176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
580c0d06caSMauro Carvalho Chehab 		.bytesperline = 176,
590c0d06caSMauro Carvalho Chehab 		.sizeimage = 176 * 144,
600c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
610c0d06caSMauro Carvalho Chehab 		.priv = 0},
620c0d06caSMauro Carvalho Chehab 	{352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
630c0d06caSMauro Carvalho Chehab 		.bytesperline = 352,
640c0d06caSMauro Carvalho Chehab 		.sizeimage = 352 * 288,
650c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
660c0d06caSMauro Carvalho Chehab 		.priv = 0},
670c0d06caSMauro Carvalho Chehab };
680c0d06caSMauro Carvalho Chehab 
690c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format vga_mode[] = {
700c0d06caSMauro Carvalho Chehab 	{320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
710c0d06caSMauro Carvalho Chehab 		.bytesperline = 320,
720c0d06caSMauro Carvalho Chehab 		.sizeimage = 320 * 240,
730c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
740c0d06caSMauro Carvalho Chehab 		.priv = 0},
750c0d06caSMauro Carvalho Chehab 	{640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
760c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
770c0d06caSMauro Carvalho Chehab 		.sizeimage = 640 * 480,
780c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
790c0d06caSMauro Carvalho Chehab 		.priv = 0},
800c0d06caSMauro Carvalho Chehab };
810c0d06caSMauro Carvalho Chehab 
820c0d06caSMauro Carvalho Chehab /*
830c0d06caSMauro Carvalho Chehab  * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
840c0d06caSMauro Carvalho Chehab  * and 0x82 for bulk data transfer.
850c0d06caSMauro Carvalho Chehab  */
860c0d06caSMauro Carvalho Chehab 
870c0d06caSMauro Carvalho Chehab /* All commands are two bytes only */
880c0d06caSMauro Carvalho Chehab static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
890c0d06caSMauro Carvalho Chehab {
900c0d06caSMauro Carvalho Chehab 	int retval;
910c0d06caSMauro Carvalho Chehab 
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 	return retval;
1000c0d06caSMauro Carvalho Chehab }
1010c0d06caSMauro Carvalho Chehab 
1020c0d06caSMauro Carvalho Chehab /* Response to a command is one byte in usb_buf[0], only if requested. */
1030c0d06caSMauro Carvalho Chehab static int jl2005c_read1(struct gspca_dev *gspca_dev)
1040c0d06caSMauro Carvalho Chehab {
1050c0d06caSMauro Carvalho Chehab 	int retval;
1060c0d06caSMauro Carvalho Chehab 
1070c0d06caSMauro Carvalho Chehab 	retval = usb_bulk_msg(gspca_dev->dev,
1080c0d06caSMauro Carvalho Chehab 				usb_rcvbulkpipe(gspca_dev->dev, 0x84),
1090c0d06caSMauro Carvalho Chehab 				gspca_dev->usb_buf, 1, NULL, 500);
1100c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1110c0d06caSMauro Carvalho Chehab 		pr_err("read command [0x%02x] error %d\n",
1120c0d06caSMauro Carvalho Chehab 		       gspca_dev->usb_buf[0], retval);
1130c0d06caSMauro Carvalho Chehab 	return retval;
1140c0d06caSMauro Carvalho Chehab }
1150c0d06caSMauro Carvalho Chehab 
1160c0d06caSMauro Carvalho Chehab /* Response appears in gspca_dev->usb_buf[0] */
1170c0d06caSMauro Carvalho Chehab static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
1180c0d06caSMauro Carvalho Chehab {
1190c0d06caSMauro Carvalho Chehab 	int retval;
1200c0d06caSMauro Carvalho Chehab 
1210c0d06caSMauro Carvalho Chehab 	static u8 instruction[2] = {0x95, 0x00};
1220c0d06caSMauro Carvalho Chehab 	/* put register to read in byte 1 */
1230c0d06caSMauro Carvalho Chehab 	instruction[1] = reg;
1240c0d06caSMauro Carvalho Chehab 	/* Send the read request */
1250c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write2(gspca_dev, instruction);
1260c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1270c0d06caSMauro Carvalho Chehab 		return retval;
1280c0d06caSMauro Carvalho Chehab 	retval = jl2005c_read1(gspca_dev);
1290c0d06caSMauro Carvalho Chehab 
1300c0d06caSMauro Carvalho Chehab 	return retval;
1310c0d06caSMauro Carvalho Chehab }
1320c0d06caSMauro Carvalho Chehab 
1330c0d06caSMauro Carvalho Chehab static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
1340c0d06caSMauro Carvalho Chehab {
1350c0d06caSMauro Carvalho Chehab 	int i;
1360c0d06caSMauro Carvalho Chehab 	int retval;
1370c0d06caSMauro Carvalho Chehab 	int frame_brightness = 0;
1380c0d06caSMauro Carvalho Chehab 
1390c0d06caSMauro Carvalho Chehab 	static u8 instruction[2] = {0x7f, 0x01};
1400c0d06caSMauro Carvalho Chehab 
1410c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write2(gspca_dev, instruction);
1420c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1430c0d06caSMauro Carvalho Chehab 		return retval;
1440c0d06caSMauro Carvalho Chehab 
1450c0d06caSMauro Carvalho Chehab 	i = 0;
1460c0d06caSMauro Carvalho Chehab 	while (i < 20 && !frame_brightness) {
1470c0d06caSMauro Carvalho Chehab 		/* If we tried 20 times, give up. */
1480c0d06caSMauro Carvalho Chehab 		retval = jl2005c_read_reg(gspca_dev, 0x7e);
1490c0d06caSMauro Carvalho Chehab 		if (retval < 0)
1500c0d06caSMauro Carvalho Chehab 			return retval;
1510c0d06caSMauro Carvalho Chehab 		frame_brightness = gspca_dev->usb_buf[0];
1520c0d06caSMauro Carvalho Chehab 		retval = jl2005c_read_reg(gspca_dev, 0x7d);
1530c0d06caSMauro Carvalho Chehab 		if (retval < 0)
1540c0d06caSMauro Carvalho Chehab 			return retval;
1550c0d06caSMauro Carvalho Chehab 		i++;
1560c0d06caSMauro Carvalho Chehab 	}
1570c0d06caSMauro Carvalho Chehab 	PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]);
1580c0d06caSMauro Carvalho Chehab 	return retval;
1590c0d06caSMauro Carvalho Chehab }
1600c0d06caSMauro Carvalho Chehab 
1610c0d06caSMauro Carvalho Chehab static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
1620c0d06caSMauro Carvalho Chehab 						    unsigned char value)
1630c0d06caSMauro Carvalho Chehab {
1640c0d06caSMauro Carvalho Chehab 	int retval;
1650c0d06caSMauro Carvalho Chehab 	u8 instruction[2];
1660c0d06caSMauro Carvalho Chehab 
1670c0d06caSMauro Carvalho Chehab 	instruction[0] = reg;
1680c0d06caSMauro Carvalho Chehab 	instruction[1] = value;
1690c0d06caSMauro Carvalho Chehab 
1700c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write2(gspca_dev, instruction);
1710c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1720c0d06caSMauro Carvalho Chehab 			return retval;
1730c0d06caSMauro Carvalho Chehab 
1740c0d06caSMauro Carvalho Chehab 	return retval;
1750c0d06caSMauro Carvalho Chehab }
1760c0d06caSMauro Carvalho Chehab 
1770c0d06caSMauro Carvalho Chehab static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
1780c0d06caSMauro Carvalho Chehab {
1790c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
1800c0d06caSMauro Carvalho Chehab 	int i = 0;
1810c0d06caSMauro Carvalho Chehab 	int retval = -1;
1820c0d06caSMauro Carvalho Chehab 	unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f};
1830c0d06caSMauro Carvalho Chehab 
1840c0d06caSMauro Carvalho Chehab 	PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id");
1850c0d06caSMauro Carvalho Chehab 	/* Read the first ID byte once for warmup */
1860c0d06caSMauro Carvalho Chehab 	retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
1870c0d06caSMauro Carvalho Chehab 	PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]);
1880c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1890c0d06caSMauro Carvalho Chehab 		return retval;
1900c0d06caSMauro Carvalho Chehab 	/* Now actually get the ID string */
1910c0d06caSMauro Carvalho Chehab 	for (i = 0; i < 6; i++) {
1920c0d06caSMauro Carvalho Chehab 		retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
1930c0d06caSMauro Carvalho Chehab 		if (retval < 0)
1940c0d06caSMauro Carvalho Chehab 			return retval;
1950c0d06caSMauro Carvalho Chehab 		sd->firmware_id[i] = gspca_dev->usb_buf[0];
1960c0d06caSMauro Carvalho Chehab 	}
1970c0d06caSMauro Carvalho Chehab 	PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x",
1980c0d06caSMauro Carvalho Chehab 						sd->firmware_id[0],
1990c0d06caSMauro Carvalho Chehab 						sd->firmware_id[1],
2000c0d06caSMauro Carvalho Chehab 						sd->firmware_id[2],
2010c0d06caSMauro Carvalho Chehab 						sd->firmware_id[3],
2020c0d06caSMauro Carvalho Chehab 						sd->firmware_id[4],
2030c0d06caSMauro Carvalho Chehab 						sd->firmware_id[5]);
2040c0d06caSMauro Carvalho Chehab 	return 0;
2050c0d06caSMauro Carvalho Chehab }
2060c0d06caSMauro Carvalho Chehab 
2070c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_vga_lg
2080c0d06caSMauro Carvalho Chehab 		    (struct gspca_dev *gspca_dev)
2090c0d06caSMauro Carvalho Chehab {
2100c0d06caSMauro Carvalho Chehab 	int i;
2110c0d06caSMauro Carvalho Chehab 	int retval = -1;
2120c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2130c0d06caSMauro Carvalho Chehab 		{0x05, 0x00},
2140c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2150c0d06caSMauro Carvalho Chehab 		{0x7d, 0x18},
2160c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2170c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2180c0d06caSMauro Carvalho Chehab 		{0x04, 0x52},
2190c0d06caSMauro Carvalho Chehab 	};
2200c0d06caSMauro Carvalho Chehab 
2210c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2220c0d06caSMauro Carvalho Chehab 		msleep(60);
2230c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2240c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2250c0d06caSMauro Carvalho Chehab 			return retval;
2260c0d06caSMauro Carvalho Chehab 	}
2270c0d06caSMauro Carvalho Chehab 	msleep(60);
2280c0d06caSMauro Carvalho Chehab 	return retval;
2290c0d06caSMauro Carvalho Chehab }
2300c0d06caSMauro Carvalho Chehab 
2310c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
2320c0d06caSMauro Carvalho Chehab {
2330c0d06caSMauro Carvalho Chehab 	int i;
2340c0d06caSMauro Carvalho Chehab 	int retval = -1;
2350c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2360c0d06caSMauro Carvalho Chehab 		{0x06, 0x00},
2370c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2380c0d06caSMauro Carvalho Chehab 		{0x7d, 0x1a},
2390c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2400c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2410c0d06caSMauro Carvalho Chehab 		{0x04, 0x52},
2420c0d06caSMauro Carvalho Chehab 	};
2430c0d06caSMauro Carvalho Chehab 
2440c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2450c0d06caSMauro Carvalho Chehab 		msleep(60);
2460c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2470c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2480c0d06caSMauro Carvalho Chehab 			return retval;
2490c0d06caSMauro Carvalho Chehab 	}
2500c0d06caSMauro Carvalho Chehab 	msleep(60);
2510c0d06caSMauro Carvalho Chehab 	return retval;
2520c0d06caSMauro Carvalho Chehab }
2530c0d06caSMauro Carvalho Chehab 
2540c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
2550c0d06caSMauro Carvalho Chehab {
2560c0d06caSMauro Carvalho Chehab 	int i;
2570c0d06caSMauro Carvalho Chehab 	int retval = -1;
2580c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2590c0d06caSMauro Carvalho Chehab 		{0x05, 0x00},
2600c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2610c0d06caSMauro Carvalho Chehab 		{0x7d, 0x30},
2620c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2630c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2640c0d06caSMauro Carvalho Chehab 		{0x04, 0x42},
2650c0d06caSMauro Carvalho Chehab 	};
2660c0d06caSMauro Carvalho Chehab 
2670c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2680c0d06caSMauro Carvalho Chehab 		msleep(60);
2690c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2700c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2710c0d06caSMauro Carvalho Chehab 			return retval;
2720c0d06caSMauro Carvalho Chehab 	}
2730c0d06caSMauro Carvalho Chehab 	msleep(60);
2740c0d06caSMauro Carvalho Chehab 	return retval;
2750c0d06caSMauro Carvalho Chehab }
2760c0d06caSMauro Carvalho Chehab 
2770c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
2780c0d06caSMauro Carvalho Chehab {
2790c0d06caSMauro Carvalho Chehab 	int i;
2800c0d06caSMauro Carvalho Chehab 	int retval = -1;
2810c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2820c0d06caSMauro Carvalho Chehab 		{0x06, 0x00},
2830c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2840c0d06caSMauro Carvalho Chehab 		{0x7d, 0x32},
2850c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2860c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2870c0d06caSMauro Carvalho Chehab 		{0x04, 0x42},
2880c0d06caSMauro Carvalho Chehab 	};
2890c0d06caSMauro Carvalho Chehab 
2900c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2910c0d06caSMauro Carvalho Chehab 		msleep(60);
2920c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2930c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2940c0d06caSMauro Carvalho Chehab 			return retval;
2950c0d06caSMauro Carvalho Chehab 	}
2960c0d06caSMauro Carvalho Chehab 	msleep(60);
2970c0d06caSMauro Carvalho Chehab 	return retval;
2980c0d06caSMauro Carvalho Chehab }
2990c0d06caSMauro Carvalho Chehab 
3000c0d06caSMauro Carvalho Chehab 
3010c0d06caSMauro Carvalho Chehab static int jl2005c_stop(struct gspca_dev *gspca_dev)
3020c0d06caSMauro Carvalho Chehab {
3030c0d06caSMauro Carvalho Chehab 	int retval;
3040c0d06caSMauro Carvalho Chehab 
3050c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00);
3060c0d06caSMauro Carvalho Chehab 	return retval;
3070c0d06caSMauro Carvalho Chehab }
3080c0d06caSMauro Carvalho Chehab 
309*844db450SHans de Goede /*
310*844db450SHans de Goede  * This function is called as a workqueue function and runs whenever the camera
3110c0d06caSMauro Carvalho Chehab  * is streaming data. Because it is a workqueue function it is allowed to sleep
3120c0d06caSMauro Carvalho Chehab  * so we can use synchronous USB calls. To avoid possible collisions with other
313*844db450SHans de Goede  * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
314*844db450SHans de Goede  * performing USB operations using it. In practice we don't really need this
315*844db450SHans de Goede  * as the camera doesn't provide any controls.
3160c0d06caSMauro Carvalho Chehab  */
3170c0d06caSMauro Carvalho Chehab static void jl2005c_dostream(struct work_struct *work)
3180c0d06caSMauro Carvalho Chehab {
3190c0d06caSMauro Carvalho Chehab 	struct sd *dev = container_of(work, struct sd, work_struct);
3200c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev = &dev->gspca_dev;
3210c0d06caSMauro Carvalho Chehab 	int bytes_left = 0; /* bytes remaining in current frame. */
3220c0d06caSMauro Carvalho Chehab 	int data_len;   /* size to use for the next read. */
3230c0d06caSMauro Carvalho Chehab 	int header_read = 0;
3240c0d06caSMauro Carvalho Chehab 	unsigned char header_sig[2] = {0x4a, 0x4c};
3250c0d06caSMauro Carvalho Chehab 	int act_len;
3260c0d06caSMauro Carvalho Chehab 	int packet_type;
3270c0d06caSMauro Carvalho Chehab 	int ret;
3280c0d06caSMauro Carvalho Chehab 	u8 *buffer;
3290c0d06caSMauro Carvalho Chehab 
3300c0d06caSMauro Carvalho Chehab 	buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
3310c0d06caSMauro Carvalho Chehab 	if (!buffer) {
3320c0d06caSMauro Carvalho Chehab 		pr_err("Couldn't allocate USB buffer\n");
3330c0d06caSMauro Carvalho Chehab 		goto quit_stream;
3340c0d06caSMauro Carvalho Chehab 	}
3350c0d06caSMauro Carvalho Chehab 
336345321dcSHans de Goede 	while (gspca_dev->present && gspca_dev->streaming) {
3370c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
3380c0d06caSMauro Carvalho Chehab 		if (gspca_dev->frozen)
3390c0d06caSMauro Carvalho Chehab 			break;
3400c0d06caSMauro Carvalho Chehab #endif
3410c0d06caSMauro Carvalho Chehab 		/* Check if this is a new frame. If so, start the frame first */
3420c0d06caSMauro Carvalho Chehab 		if (!header_read) {
3430c0d06caSMauro Carvalho Chehab 			mutex_lock(&gspca_dev->usb_lock);
3440c0d06caSMauro Carvalho Chehab 			ret = jl2005c_start_new_frame(gspca_dev);
3450c0d06caSMauro Carvalho Chehab 			mutex_unlock(&gspca_dev->usb_lock);
3460c0d06caSMauro Carvalho Chehab 			if (ret < 0)
3470c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3480c0d06caSMauro Carvalho Chehab 			ret = usb_bulk_msg(gspca_dev->dev,
3490c0d06caSMauro Carvalho Chehab 				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
3500c0d06caSMauro Carvalho Chehab 				buffer, JL2005C_MAX_TRANSFER, &act_len,
3510c0d06caSMauro Carvalho Chehab 				JL2005C_DATA_TIMEOUT);
3520c0d06caSMauro Carvalho Chehab 			PDEBUG(D_PACK,
3530c0d06caSMauro Carvalho Chehab 				"Got %d bytes out of %d for header",
3540c0d06caSMauro Carvalho Chehab 					act_len, JL2005C_MAX_TRANSFER);
3550c0d06caSMauro Carvalho Chehab 			if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
3560c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3570c0d06caSMauro Carvalho Chehab 			/* Check whether we actually got the first blodk */
3580c0d06caSMauro Carvalho Chehab 			if (memcmp(header_sig, buffer, 2) != 0) {
3590c0d06caSMauro Carvalho Chehab 				pr_err("First block is not the first block\n");
3600c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3610c0d06caSMauro Carvalho Chehab 			}
3620c0d06caSMauro Carvalho Chehab 			/* total size to fetch is byte 7, times blocksize
3630c0d06caSMauro Carvalho Chehab 			 * of which we already got act_len */
3640c0d06caSMauro Carvalho Chehab 			bytes_left = buffer[0x07] * dev->block_size - act_len;
3650c0d06caSMauro Carvalho Chehab 			PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left);
3660c0d06caSMauro Carvalho Chehab 			/* We keep the header. It has other information, too.*/
3670c0d06caSMauro Carvalho Chehab 			packet_type = FIRST_PACKET;
3680c0d06caSMauro Carvalho Chehab 			gspca_frame_add(gspca_dev, packet_type,
3690c0d06caSMauro Carvalho Chehab 					buffer, act_len);
3700c0d06caSMauro Carvalho Chehab 			header_read = 1;
3710c0d06caSMauro Carvalho Chehab 		}
372345321dcSHans de Goede 		while (bytes_left > 0 && gspca_dev->present) {
3730c0d06caSMauro Carvalho Chehab 			data_len = bytes_left > JL2005C_MAX_TRANSFER ?
3740c0d06caSMauro Carvalho Chehab 				JL2005C_MAX_TRANSFER : bytes_left;
3750c0d06caSMauro Carvalho Chehab 			ret = usb_bulk_msg(gspca_dev->dev,
3760c0d06caSMauro Carvalho Chehab 				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
3770c0d06caSMauro Carvalho Chehab 				buffer, data_len, &act_len,
3780c0d06caSMauro Carvalho Chehab 				JL2005C_DATA_TIMEOUT);
3790c0d06caSMauro Carvalho Chehab 			if (ret < 0 || act_len < data_len)
3800c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3810c0d06caSMauro Carvalho Chehab 			PDEBUG(D_PACK,
3820c0d06caSMauro Carvalho Chehab 				"Got %d bytes out of %d for frame",
3830c0d06caSMauro Carvalho Chehab 						data_len, bytes_left);
3840c0d06caSMauro Carvalho Chehab 			bytes_left -= data_len;
3850c0d06caSMauro Carvalho Chehab 			if (bytes_left == 0) {
3860c0d06caSMauro Carvalho Chehab 				packet_type = LAST_PACKET;
3870c0d06caSMauro Carvalho Chehab 				header_read = 0;
3880c0d06caSMauro Carvalho Chehab 			} else
3890c0d06caSMauro Carvalho Chehab 				packet_type = INTER_PACKET;
3900c0d06caSMauro Carvalho Chehab 			gspca_frame_add(gspca_dev, packet_type,
3910c0d06caSMauro Carvalho Chehab 					buffer, data_len);
3920c0d06caSMauro Carvalho Chehab 		}
3930c0d06caSMauro Carvalho Chehab 	}
3940c0d06caSMauro Carvalho Chehab quit_stream:
395345321dcSHans de Goede 	if (gspca_dev->present) {
3960c0d06caSMauro Carvalho Chehab 		mutex_lock(&gspca_dev->usb_lock);
3970c0d06caSMauro Carvalho Chehab 		jl2005c_stop(gspca_dev);
3980c0d06caSMauro Carvalho Chehab 		mutex_unlock(&gspca_dev->usb_lock);
3990c0d06caSMauro Carvalho Chehab 	}
4000c0d06caSMauro Carvalho Chehab 	kfree(buffer);
4010c0d06caSMauro Carvalho Chehab }
4020c0d06caSMauro Carvalho Chehab 
4030c0d06caSMauro Carvalho Chehab 
4040c0d06caSMauro Carvalho Chehab 
4050c0d06caSMauro Carvalho Chehab 
4060c0d06caSMauro Carvalho Chehab /* This function is called at probe time */
4070c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev,
4080c0d06caSMauro Carvalho Chehab 			const struct usb_device_id *id)
4090c0d06caSMauro Carvalho Chehab {
4100c0d06caSMauro Carvalho Chehab 	struct cam *cam;
4110c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4120c0d06caSMauro Carvalho Chehab 
4130c0d06caSMauro Carvalho Chehab 	cam = &gspca_dev->cam;
4140c0d06caSMauro Carvalho Chehab 	/* We don't use the buffer gspca allocates so make it small. */
4150c0d06caSMauro Carvalho Chehab 	cam->bulk_size = 64;
4160c0d06caSMauro Carvalho Chehab 	cam->bulk = 1;
4170c0d06caSMauro Carvalho Chehab 	/* For the rest, the camera needs to be detected */
4180c0d06caSMauro Carvalho Chehab 	jl2005c_get_firmware_id(gspca_dev);
4190c0d06caSMauro Carvalho Chehab 	/* Here are some known firmware IDs
4200c0d06caSMauro Carvalho Chehab 	 * First some JL2005B cameras
4210c0d06caSMauro Carvalho Chehab 	 * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2}	Sakar KidzCam
4220c0d06caSMauro Carvalho Chehab 	 * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2}	No-name JL2005B
4230c0d06caSMauro Carvalho Chehab 	 * JL2005C cameras
4240c0d06caSMauro Carvalho Chehab 	 * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8}	Argus DC-1512
4250c0d06caSMauro Carvalho Chehab 	 * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8}	ICarly
4260c0d06caSMauro Carvalho Chehab 	 * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4}	Jazz
4270c0d06caSMauro Carvalho Chehab 	 *
4280c0d06caSMauro Carvalho Chehab 	 * Based upon this scanty evidence, we can detect a CIF camera by
4290c0d06caSMauro Carvalho Chehab 	 * testing byte 0 for 0x4x.
4300c0d06caSMauro Carvalho Chehab 	 */
4310c0d06caSMauro Carvalho Chehab 	if ((sd->firmware_id[0] & 0xf0) == 0x40) {
4320c0d06caSMauro Carvalho Chehab 		cam->cam_mode	= cif_mode;
4330c0d06caSMauro Carvalho Chehab 		cam->nmodes	= ARRAY_SIZE(cif_mode);
4340c0d06caSMauro Carvalho Chehab 		sd->block_size	= 0x80;
4350c0d06caSMauro Carvalho Chehab 	} else {
4360c0d06caSMauro Carvalho Chehab 		cam->cam_mode	= vga_mode;
4370c0d06caSMauro Carvalho Chehab 		cam->nmodes	= ARRAY_SIZE(vga_mode);
4380c0d06caSMauro Carvalho Chehab 		sd->block_size	= 0x200;
4390c0d06caSMauro Carvalho Chehab 	}
4400c0d06caSMauro Carvalho Chehab 
4410c0d06caSMauro Carvalho Chehab 	INIT_WORK(&sd->work_struct, jl2005c_dostream);
4420c0d06caSMauro Carvalho Chehab 
4430c0d06caSMauro Carvalho Chehab 	return 0;
4440c0d06caSMauro Carvalho Chehab }
4450c0d06caSMauro Carvalho Chehab 
4460c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */
4470c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev)
4480c0d06caSMauro Carvalho Chehab {
4490c0d06caSMauro Carvalho Chehab 	return 0;
4500c0d06caSMauro Carvalho Chehab }
4510c0d06caSMauro Carvalho Chehab 
4520c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev)
4530c0d06caSMauro Carvalho Chehab {
4540c0d06caSMauro Carvalho Chehab 
4550c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4560c0d06caSMauro Carvalho Chehab 	sd->cap_mode = gspca_dev->cam.cam_mode;
4570c0d06caSMauro Carvalho Chehab 
4580c0d06caSMauro Carvalho Chehab 	switch (gspca_dev->width) {
4590c0d06caSMauro Carvalho Chehab 	case 640:
4600c0d06caSMauro Carvalho Chehab 		PDEBUG(D_STREAM, "Start streaming at vga resolution");
4610c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_vga_lg(gspca_dev);
4620c0d06caSMauro Carvalho Chehab 		break;
4630c0d06caSMauro Carvalho Chehab 	case 320:
4640c0d06caSMauro Carvalho Chehab 		PDEBUG(D_STREAM, "Start streaming at qvga resolution");
4650c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_vga_small(gspca_dev);
4660c0d06caSMauro Carvalho Chehab 		break;
4670c0d06caSMauro Carvalho Chehab 	case 352:
4680c0d06caSMauro Carvalho Chehab 		PDEBUG(D_STREAM, "Start streaming at cif resolution");
4690c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_cif_lg(gspca_dev);
4700c0d06caSMauro Carvalho Chehab 		break;
4710c0d06caSMauro Carvalho Chehab 	case 176:
4720c0d06caSMauro Carvalho Chehab 		PDEBUG(D_STREAM, "Start streaming at qcif resolution");
4730c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_cif_small(gspca_dev);
4740c0d06caSMauro Carvalho Chehab 		break;
4750c0d06caSMauro Carvalho Chehab 	default:
4760c0d06caSMauro Carvalho Chehab 		pr_err("Unknown resolution specified\n");
4770c0d06caSMauro Carvalho Chehab 		return -1;
4780c0d06caSMauro Carvalho Chehab 	}
4790c0d06caSMauro Carvalho Chehab 
4800c0d06caSMauro Carvalho Chehab 	/* Start the workqueue function to do the streaming */
4810c0d06caSMauro Carvalho Chehab 	sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
4820c0d06caSMauro Carvalho Chehab 	queue_work(sd->work_thread, &sd->work_struct);
4830c0d06caSMauro Carvalho Chehab 
4840c0d06caSMauro Carvalho Chehab 	return 0;
4850c0d06caSMauro Carvalho Chehab }
4860c0d06caSMauro Carvalho Chehab 
4870c0d06caSMauro Carvalho Chehab /* called on streamoff with alt==0 and on disconnect */
4880c0d06caSMauro Carvalho Chehab /* the usb_lock is held at entry - restore on exit */
4890c0d06caSMauro Carvalho Chehab static void sd_stop0(struct gspca_dev *gspca_dev)
4900c0d06caSMauro Carvalho Chehab {
4910c0d06caSMauro Carvalho Chehab 	struct sd *dev = (struct sd *) gspca_dev;
4920c0d06caSMauro Carvalho Chehab 
4930c0d06caSMauro Carvalho Chehab 	/* wait for the work queue to terminate */
4940c0d06caSMauro Carvalho Chehab 	mutex_unlock(&gspca_dev->usb_lock);
4950c0d06caSMauro Carvalho Chehab 	/* This waits for sq905c_dostream to finish */
4960c0d06caSMauro Carvalho Chehab 	destroy_workqueue(dev->work_thread);
4970c0d06caSMauro Carvalho Chehab 	dev->work_thread = NULL;
4980c0d06caSMauro Carvalho Chehab 	mutex_lock(&gspca_dev->usb_lock);
4990c0d06caSMauro Carvalho Chehab }
5000c0d06caSMauro Carvalho Chehab 
5010c0d06caSMauro Carvalho Chehab 
5020c0d06caSMauro Carvalho Chehab 
5030c0d06caSMauro Carvalho Chehab /* sub-driver description */
5040c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc = {
5050c0d06caSMauro Carvalho Chehab 	.name = MODULE_NAME,
5060c0d06caSMauro Carvalho Chehab 	.config = sd_config,
5070c0d06caSMauro Carvalho Chehab 	.init = sd_init,
5080c0d06caSMauro Carvalho Chehab 	.start = sd_start,
5090c0d06caSMauro Carvalho Chehab 	.stop0 = sd_stop0,
5100c0d06caSMauro Carvalho Chehab };
5110c0d06caSMauro Carvalho Chehab 
5120c0d06caSMauro Carvalho Chehab /* -- module initialisation -- */
51379e8c7beSMauro Carvalho Chehab static const struct usb_device_id device_table[] = {
5140c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x0979, 0x0227)},
5150c0d06caSMauro Carvalho Chehab 	{}
5160c0d06caSMauro Carvalho Chehab };
5170c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table);
5180c0d06caSMauro Carvalho Chehab 
5190c0d06caSMauro Carvalho Chehab /* -- device connect -- */
5200c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf,
5210c0d06caSMauro Carvalho Chehab 				const struct usb_device_id *id)
5220c0d06caSMauro Carvalho Chehab {
5230c0d06caSMauro Carvalho Chehab 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
5240c0d06caSMauro Carvalho Chehab 				THIS_MODULE);
5250c0d06caSMauro Carvalho Chehab }
5260c0d06caSMauro Carvalho Chehab 
5270c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = {
5280c0d06caSMauro Carvalho Chehab 	.name = MODULE_NAME,
5290c0d06caSMauro Carvalho Chehab 	.id_table = device_table,
5300c0d06caSMauro Carvalho Chehab 	.probe = sd_probe,
5310c0d06caSMauro Carvalho Chehab 	.disconnect = gspca_disconnect,
5320c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
5330c0d06caSMauro Carvalho Chehab 	.suspend = gspca_suspend,
5340c0d06caSMauro Carvalho Chehab 	.resume = gspca_resume,
5350c0d06caSMauro Carvalho Chehab 	.reset_resume = gspca_resume,
5360c0d06caSMauro Carvalho Chehab #endif
5370c0d06caSMauro Carvalho Chehab };
5380c0d06caSMauro Carvalho Chehab 
5390c0d06caSMauro Carvalho Chehab /* -- module insert / remove -- */
5400c0d06caSMauro Carvalho Chehab static int __init sd_mod_init(void)
5410c0d06caSMauro Carvalho Chehab {
5420c0d06caSMauro Carvalho Chehab 	int ret;
5430c0d06caSMauro Carvalho Chehab 
5440c0d06caSMauro Carvalho Chehab 	ret = usb_register(&sd_driver);
5450c0d06caSMauro Carvalho Chehab 	if (ret < 0)
5460c0d06caSMauro Carvalho Chehab 		return ret;
5470c0d06caSMauro Carvalho Chehab 	return 0;
5480c0d06caSMauro Carvalho Chehab }
5490c0d06caSMauro Carvalho Chehab static void __exit sd_mod_exit(void)
5500c0d06caSMauro Carvalho Chehab {
5510c0d06caSMauro Carvalho Chehab 	usb_deregister(&sd_driver);
5520c0d06caSMauro Carvalho Chehab }
5530c0d06caSMauro Carvalho Chehab 
5540c0d06caSMauro Carvalho Chehab module_init(sd_mod_init);
5550c0d06caSMauro Carvalho Chehab module_exit(sd_mod_exit);
556