xref: /openbmc/linux/drivers/media/usb/s2255/s2255drv.c (revision 92cde477c0cb7fc46c4428145d91f53bb5ffc46a)
10aa77f6cSMauro Carvalho Chehab /*
20aa77f6cSMauro Carvalho Chehab  *  s2255drv.c - a driver for the Sensoray 2255 USB video capture device
30aa77f6cSMauro Carvalho Chehab  *
4d86c6a8cSDean Anderson  *   Copyright (C) 2007-2014 by Sensoray Company Inc.
50aa77f6cSMauro Carvalho Chehab  *                              Dean Anderson
60aa77f6cSMauro Carvalho Chehab  *
70aa77f6cSMauro Carvalho Chehab  * Some video buffer code based on vivi driver:
80aa77f6cSMauro Carvalho Chehab  *
90aa77f6cSMauro Carvalho Chehab  * Sensoray 2255 device supports 4 simultaneous channels.
100aa77f6cSMauro Carvalho Chehab  * The channels are not "crossbar" inputs, they are physically
110aa77f6cSMauro Carvalho Chehab  * attached to separate video decoders.
120aa77f6cSMauro Carvalho Chehab  *
130aa77f6cSMauro Carvalho Chehab  * Because of USB2.0 bandwidth limitations. There is only a
140aa77f6cSMauro Carvalho Chehab  * certain amount of data which may be transferred at one time.
150aa77f6cSMauro Carvalho Chehab  *
160aa77f6cSMauro Carvalho Chehab  * Example maximum bandwidth utilization:
170aa77f6cSMauro Carvalho Chehab  *
180aa77f6cSMauro Carvalho Chehab  * -full size, color mode YUYV or YUV422P: 2 channels at once
190aa77f6cSMauro Carvalho Chehab  * -full or half size Grey scale: all 4 channels at once
200aa77f6cSMauro Carvalho Chehab  * -half size, color mode YUYV or YUV422P: all 4 channels at once
210aa77f6cSMauro Carvalho Chehab  * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
220aa77f6cSMauro Carvalho Chehab  *  at once.
230aa77f6cSMauro Carvalho Chehab  *
240aa77f6cSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
250aa77f6cSMauro Carvalho Chehab  * it under the terms of the GNU General Public License as published by
260aa77f6cSMauro Carvalho Chehab  * the Free Software Foundation; either version 2 of the License, or
270aa77f6cSMauro Carvalho Chehab  * (at your option) any later version.
280aa77f6cSMauro Carvalho Chehab  *
290aa77f6cSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
300aa77f6cSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
310aa77f6cSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
320aa77f6cSMauro Carvalho Chehab  * GNU General Public License for more details.
330aa77f6cSMauro Carvalho Chehab  *
340aa77f6cSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
350aa77f6cSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
360aa77f6cSMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
370aa77f6cSMauro Carvalho Chehab  */
380aa77f6cSMauro Carvalho Chehab 
390aa77f6cSMauro Carvalho Chehab #include <linux/module.h>
400aa77f6cSMauro Carvalho Chehab #include <linux/firmware.h>
410aa77f6cSMauro Carvalho Chehab #include <linux/kernel.h>
420aa77f6cSMauro Carvalho Chehab #include <linux/mutex.h>
430aa77f6cSMauro Carvalho Chehab #include <linux/slab.h>
440aa77f6cSMauro Carvalho Chehab #include <linux/videodev2.h>
450aa77f6cSMauro Carvalho Chehab #include <linux/mm.h>
4644d06d82SHans Verkuil #include <linux/vmalloc.h>
4744d06d82SHans Verkuil #include <linux/usb.h>
480aa77f6cSMauro Carvalho Chehab #include <media/videobuf-vmalloc.h>
490aa77f6cSMauro Carvalho Chehab #include <media/v4l2-common.h>
500aa77f6cSMauro Carvalho Chehab #include <media/v4l2-device.h>
510aa77f6cSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
52192f1e78SHans Verkuil #include <media/v4l2-ctrls.h>
5344d06d82SHans Verkuil #include <media/v4l2-event.h>
540aa77f6cSMauro Carvalho Chehab 
55d86c6a8cSDean Anderson #define S2255_VERSION		"1.24.1"
560aa77f6cSMauro Carvalho Chehab #define FIRMWARE_FILE_NAME "f2255usb.bin"
570aa77f6cSMauro Carvalho Chehab 
580aa77f6cSMauro Carvalho Chehab /* default JPEG quality */
590aa77f6cSMauro Carvalho Chehab #define S2255_DEF_JPEG_QUAL     50
600aa77f6cSMauro Carvalho Chehab /* vendor request in */
610aa77f6cSMauro Carvalho Chehab #define S2255_VR_IN		0
620aa77f6cSMauro Carvalho Chehab /* vendor request out */
630aa77f6cSMauro Carvalho Chehab #define S2255_VR_OUT		1
640aa77f6cSMauro Carvalho Chehab /* firmware query */
650aa77f6cSMauro Carvalho Chehab #define S2255_VR_FW		0x30
660aa77f6cSMauro Carvalho Chehab /* USB endpoint number for configuring the device */
670aa77f6cSMauro Carvalho Chehab #define S2255_CONFIG_EP         2
680aa77f6cSMauro Carvalho Chehab /* maximum time for DSP to start responding after last FW word loaded(ms) */
690aa77f6cSMauro Carvalho Chehab #define S2255_DSP_BOOTTIME      800
700aa77f6cSMauro Carvalho Chehab /* maximum time to wait for firmware to load (ms) */
710aa77f6cSMauro Carvalho Chehab #define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
729da62eb0SDean Anderson #define S2255_MIN_BUFS          2
730aa77f6cSMauro Carvalho Chehab #define S2255_SETMODE_TIMEOUT   500
740aa77f6cSMauro Carvalho Chehab #define S2255_VIDSTATUS_TIMEOUT 350
750aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_FRAME	cpu_to_le32(0x2255DA4AL)
760aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_RESPONSE	cpu_to_le32(0x2255ACACL)
770aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_SETMODE  cpu_to_le32(0x01)
780aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_FW       cpu_to_le32(0x10)
790aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_STATUS   cpu_to_le32(0x20)
800aa77f6cSMauro Carvalho Chehab #define S2255_USB_XFER_SIZE	(16 * 1024)
810aa77f6cSMauro Carvalho Chehab #define MAX_CHANNELS		4
820aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES		4
830aa77f6cSMauro Carvalho Chehab /* maximum size is PAL full size plus room for the marker header(s) */
840aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
850aa77f6cSMauro Carvalho Chehab #define DEF_USB_BLOCK		S2255_USB_XFER_SIZE
860aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_NTSC	640
870aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_NTSC	640
880aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_NTSC	320
890aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_PAL	704
900aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_PAL	704
910aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_PAL	352
920aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_NTSC	240
930aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_NTSC	240
940aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_NTSC	240
950aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_PAL	288
960aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_PAL	288
970aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_PAL	288
980aa77f6cSMauro Carvalho Chehab #define LINE_SZ_DEF		640
990aa77f6cSMauro Carvalho Chehab #define NUM_LINES_DEF		240
1000aa77f6cSMauro Carvalho Chehab 
1010aa77f6cSMauro Carvalho Chehab 
1020aa77f6cSMauro Carvalho Chehab /* predefined settings */
1030aa77f6cSMauro Carvalho Chehab #define FORMAT_NTSC	1
1040aa77f6cSMauro Carvalho Chehab #define FORMAT_PAL	2
1050aa77f6cSMauro Carvalho Chehab 
1060aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
1070aa77f6cSMauro Carvalho Chehab #define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
1080aa77f6cSMauro Carvalho Chehab #define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
1090aa77f6cSMauro Carvalho Chehab /* SCALE_4CIFSI is the 2 fields interpolated into one */
1100aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFSI	4	/* 640x480(NTSC) or 704x576(PAL) high quality */
1110aa77f6cSMauro Carvalho Chehab 
1120aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPL	1	/* YUV planar */
1130aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPK	2	/* YUV packed */
1140aa77f6cSMauro Carvalho Chehab #define COLOR_Y8	4	/* monochrome */
1150aa77f6cSMauro Carvalho Chehab #define COLOR_JPG       5       /* JPEG */
1160aa77f6cSMauro Carvalho Chehab 
1170aa77f6cSMauro Carvalho Chehab #define MASK_COLOR       0x000000ff
1180aa77f6cSMauro Carvalho Chehab #define MASK_JPG_QUALITY 0x0000ff00
1190aa77f6cSMauro Carvalho Chehab #define MASK_INPUT_TYPE  0x000f0000
1200aa77f6cSMauro Carvalho Chehab /* frame decimation. */
1210aa77f6cSMauro Carvalho Chehab #define FDEC_1		1	/* capture every frame. default */
1220aa77f6cSMauro Carvalho Chehab #define FDEC_2		2	/* capture every 2nd frame */
1230aa77f6cSMauro Carvalho Chehab #define FDEC_3		3	/* capture every 3rd frame */
1240aa77f6cSMauro Carvalho Chehab #define FDEC_5		5	/* capture every 5th frame */
1250aa77f6cSMauro Carvalho Chehab 
1260aa77f6cSMauro Carvalho Chehab /*-------------------------------------------------------
1270aa77f6cSMauro Carvalho Chehab  * Default mode parameters.
1280aa77f6cSMauro Carvalho Chehab  *-------------------------------------------------------*/
1290aa77f6cSMauro Carvalho Chehab #define DEF_SCALE	SCALE_4CIFS
1300aa77f6cSMauro Carvalho Chehab #define DEF_COLOR	COLOR_YUVPL
1310aa77f6cSMauro Carvalho Chehab #define DEF_FDEC	FDEC_1
1320aa77f6cSMauro Carvalho Chehab #define DEF_BRIGHT	0
1330aa77f6cSMauro Carvalho Chehab #define DEF_CONTRAST	0x5c
1340aa77f6cSMauro Carvalho Chehab #define DEF_SATURATION	0x80
1350aa77f6cSMauro Carvalho Chehab #define DEF_HUE		0
1360aa77f6cSMauro Carvalho Chehab 
1370aa77f6cSMauro Carvalho Chehab /* usb config commands */
1380aa77f6cSMauro Carvalho Chehab #define IN_DATA_TOKEN	cpu_to_le32(0x2255c0de)
1390aa77f6cSMauro Carvalho Chehab #define CMD_2255	0xc2255000
1400aa77f6cSMauro Carvalho Chehab #define CMD_SET_MODE	cpu_to_le32((CMD_2255 | 0x10))
1410aa77f6cSMauro Carvalho Chehab #define CMD_START	cpu_to_le32((CMD_2255 | 0x20))
1420aa77f6cSMauro Carvalho Chehab #define CMD_STOP	cpu_to_le32((CMD_2255 | 0x30))
1430aa77f6cSMauro Carvalho Chehab #define CMD_STATUS	cpu_to_le32((CMD_2255 | 0x40))
1440aa77f6cSMauro Carvalho Chehab 
1450aa77f6cSMauro Carvalho Chehab struct s2255_mode {
1460aa77f6cSMauro Carvalho Chehab 	u32 format;	/* input video format (NTSC, PAL) */
1470aa77f6cSMauro Carvalho Chehab 	u32 scale;	/* output video scale */
1480aa77f6cSMauro Carvalho Chehab 	u32 color;	/* output video color format */
1490aa77f6cSMauro Carvalho Chehab 	u32 fdec;	/* frame decimation */
1500aa77f6cSMauro Carvalho Chehab 	u32 bright;	/* brightness */
1510aa77f6cSMauro Carvalho Chehab 	u32 contrast;	/* contrast */
1520aa77f6cSMauro Carvalho Chehab 	u32 saturation;	/* saturation */
1530aa77f6cSMauro Carvalho Chehab 	u32 hue;	/* hue (NTSC only)*/
1540aa77f6cSMauro Carvalho Chehab 	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
1550aa77f6cSMauro Carvalho Chehab 	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
1560aa77f6cSMauro Carvalho Chehab 	u32 restart;	/* if DSP requires restart */
1570aa77f6cSMauro Carvalho Chehab };
1580aa77f6cSMauro Carvalho Chehab 
1590aa77f6cSMauro Carvalho Chehab 
1600aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE		0
1610aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME	1
1620aa77f6cSMauro Carvalho Chehab 
1630aa77f6cSMauro Carvalho Chehab /* frame structure */
1640aa77f6cSMauro Carvalho Chehab struct s2255_framei {
1650aa77f6cSMauro Carvalho Chehab 	unsigned long size;
1660aa77f6cSMauro Carvalho Chehab 	unsigned long ulState;	/* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
1670aa77f6cSMauro Carvalho Chehab 	void *lpvbits;		/* image data */
1680aa77f6cSMauro Carvalho Chehab 	unsigned long cur_size;	/* current data copied to it */
1690aa77f6cSMauro Carvalho Chehab };
1700aa77f6cSMauro Carvalho Chehab 
1710aa77f6cSMauro Carvalho Chehab /* image buffer structure */
1720aa77f6cSMauro Carvalho Chehab struct s2255_bufferi {
1730aa77f6cSMauro Carvalho Chehab 	unsigned long dwFrames;			/* number of frames in buffer */
1740aa77f6cSMauro Carvalho Chehab 	struct s2255_framei frame[SYS_FRAMES];	/* array of FRAME structures */
1750aa77f6cSMauro Carvalho Chehab };
1760aa77f6cSMauro Carvalho Chehab 
1770aa77f6cSMauro Carvalho Chehab #define DEF_MODEI_NTSC_CONT	{FORMAT_NTSC, DEF_SCALE, DEF_COLOR,	\
1780aa77f6cSMauro Carvalho Chehab 			DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
1790aa77f6cSMauro Carvalho Chehab 			DEF_HUE, 0, DEF_USB_BLOCK, 0}
1800aa77f6cSMauro Carvalho Chehab 
1810aa77f6cSMauro Carvalho Chehab /* for firmware loading, fw_state */
1820aa77f6cSMauro Carvalho Chehab #define S2255_FW_NOTLOADED	0
1830aa77f6cSMauro Carvalho Chehab #define S2255_FW_LOADED_DSPWAIT	1
1840aa77f6cSMauro Carvalho Chehab #define S2255_FW_SUCCESS	2
1850aa77f6cSMauro Carvalho Chehab #define S2255_FW_FAILED		3
1860aa77f6cSMauro Carvalho Chehab #define S2255_FW_DISCONNECTING  4
1870aa77f6cSMauro Carvalho Chehab #define S2255_FW_MARKER		cpu_to_le32(0x22552f2f)
1880aa77f6cSMauro Carvalho Chehab /* 2255 read states */
1890aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE         0
1900aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME        1
1910aa77f6cSMauro Carvalho Chehab struct s2255_fw {
1920aa77f6cSMauro Carvalho Chehab 	int		      fw_loaded;
1930aa77f6cSMauro Carvalho Chehab 	int		      fw_size;
1940aa77f6cSMauro Carvalho Chehab 	struct urb	      *fw_urb;
1950aa77f6cSMauro Carvalho Chehab 	atomic_t	      fw_state;
1960aa77f6cSMauro Carvalho Chehab 	void		      *pfw_data;
1970aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t     wait_fw;
1980aa77f6cSMauro Carvalho Chehab 	const struct firmware *fw;
1990aa77f6cSMauro Carvalho Chehab };
2000aa77f6cSMauro Carvalho Chehab 
2010aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo {
2020aa77f6cSMauro Carvalho Chehab 	u32 max_transfer_size;
2030aa77f6cSMauro Carvalho Chehab 	u32 cur_transfer_size;
2040aa77f6cSMauro Carvalho Chehab 	u8 *transfer_buffer;
2050aa77f6cSMauro Carvalho Chehab 	u32 state;
2060aa77f6cSMauro Carvalho Chehab 	void *stream_urb;
2070aa77f6cSMauro Carvalho Chehab 	void *dev;	/* back pointer to s2255_dev struct*/
2080aa77f6cSMauro Carvalho Chehab 	u32 err_count;
2090aa77f6cSMauro Carvalho Chehab 	u32 idx;
2100aa77f6cSMauro Carvalho Chehab };
2110aa77f6cSMauro Carvalho Chehab 
2120aa77f6cSMauro Carvalho Chehab struct s2255_fmt; /*forward declaration */
2130aa77f6cSMauro Carvalho Chehab struct s2255_dev;
2140aa77f6cSMauro Carvalho Chehab 
2155e950fafSDean Anderson /* 2255 video channel */
2165e950fafSDean Anderson struct s2255_vc {
217f5402007Ssensoray-dev 	struct s2255_dev        *dev;
2180aa77f6cSMauro Carvalho Chehab 	struct video_device	vdev;
219192f1e78SHans Verkuil 	struct v4l2_ctrl_handler hdl;
2207041dec7SHans Verkuil 	struct v4l2_ctrl	*jpegqual_ctrl;
2210aa77f6cSMauro Carvalho Chehab 	int			resources;
222d86c6a8cSDean Anderson 	struct list_head        buf_list;
2230aa77f6cSMauro Carvalho Chehab 	struct s2255_bufferi	buffer;
2240aa77f6cSMauro Carvalho Chehab 	struct s2255_mode	mode;
225469af77aSHans Verkuil 	v4l2_std_id		std;
2260aa77f6cSMauro Carvalho Chehab 	/* jpeg compression */
2277041dec7SHans Verkuil 	unsigned		jpegqual;
2280aa77f6cSMauro Carvalho Chehab 	/* capture parameters (for high quality mode full size) */
2290aa77f6cSMauro Carvalho Chehab 	struct v4l2_captureparm cap_parm;
2300aa77f6cSMauro Carvalho Chehab 	int			cur_frame;
2310aa77f6cSMauro Carvalho Chehab 	int			last_frame;
2320aa77f6cSMauro Carvalho Chehab 
2330aa77f6cSMauro Carvalho Chehab 	int			b_acquire;
2340aa77f6cSMauro Carvalho Chehab 	/* allocated image size */
2350aa77f6cSMauro Carvalho Chehab 	unsigned long		req_image_size;
2360aa77f6cSMauro Carvalho Chehab 	/* received packet size */
2370aa77f6cSMauro Carvalho Chehab 	unsigned long		pkt_size;
2380aa77f6cSMauro Carvalho Chehab 	int			bad_payload;
2390aa77f6cSMauro Carvalho Chehab 	unsigned long		frame_count;
2400aa77f6cSMauro Carvalho Chehab 	/* if JPEG image */
2410aa77f6cSMauro Carvalho Chehab 	int                     jpg_size;
2420aa77f6cSMauro Carvalho Chehab 	/* if channel configured to default state */
2430aa77f6cSMauro Carvalho Chehab 	int                     configured;
2440aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_setmode;
2450aa77f6cSMauro Carvalho Chehab 	int                     setmode_ready;
2460aa77f6cSMauro Carvalho Chehab 	/* video status items */
2470aa77f6cSMauro Carvalho Chehab 	int                     vidstatus;
2480aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_vidstatus;
2490aa77f6cSMauro Carvalho Chehab 	int                     vidstatus_ready;
2500aa77f6cSMauro Carvalho Chehab 	unsigned int		width;
2510aa77f6cSMauro Carvalho Chehab 	unsigned int		height;
2520aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt	*fmt;
2530aa77f6cSMauro Carvalho Chehab 	int idx; /* channel number on device, 0-3 */
2540aa77f6cSMauro Carvalho Chehab };
2550aa77f6cSMauro Carvalho Chehab 
2560aa77f6cSMauro Carvalho Chehab 
2570aa77f6cSMauro Carvalho Chehab struct s2255_dev {
2585e950fafSDean Anderson 	struct s2255_vc         vc[MAX_CHANNELS];
2590aa77f6cSMauro Carvalho Chehab 	struct v4l2_device      v4l2_dev;
2600aa77f6cSMauro Carvalho Chehab 	atomic_t                num_channels;
2610aa77f6cSMauro Carvalho Chehab 	int			frames;
2620aa77f6cSMauro Carvalho Chehab 	struct mutex		lock;	/* channels[].vdev.lock */
26347d8c881SDean Anderson 	struct mutex		cmdlock; /* protects cmdbuf */
2640aa77f6cSMauro Carvalho Chehab 	struct usb_device	*udev;
2650aa77f6cSMauro Carvalho Chehab 	struct usb_interface	*interface;
2660aa77f6cSMauro Carvalho Chehab 	u8			read_endpoint;
2670aa77f6cSMauro Carvalho Chehab 	struct timer_list	timer;
2680aa77f6cSMauro Carvalho Chehab 	struct s2255_fw	*fw_data;
2690aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo	pipe;
2700aa77f6cSMauro Carvalho Chehab 	u32			cc;	/* current channel */
2710aa77f6cSMauro Carvalho Chehab 	int			frame_ready;
2720aa77f6cSMauro Carvalho Chehab 	int                     chn_ready;
2730aa77f6cSMauro Carvalho Chehab 	spinlock_t              slock;
2740aa77f6cSMauro Carvalho Chehab 	/* dsp firmware version (f2255usb.bin) */
2750aa77f6cSMauro Carvalho Chehab 	int                     dsp_fw_ver;
2760aa77f6cSMauro Carvalho Chehab 	u16                     pid; /* product id */
27747d8c881SDean Anderson #define S2255_CMDBUF_SIZE 512
27847d8c881SDean Anderson 	__le32                  *cmdbuf;
2790aa77f6cSMauro Carvalho Chehab };
2800aa77f6cSMauro Carvalho Chehab 
2810aa77f6cSMauro Carvalho Chehab static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
2820aa77f6cSMauro Carvalho Chehab {
2830aa77f6cSMauro Carvalho Chehab 	return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
2840aa77f6cSMauro Carvalho Chehab }
2850aa77f6cSMauro Carvalho Chehab 
2860aa77f6cSMauro Carvalho Chehab struct s2255_fmt {
2870aa77f6cSMauro Carvalho Chehab 	char *name;
2880aa77f6cSMauro Carvalho Chehab 	u32 fourcc;
2890aa77f6cSMauro Carvalho Chehab 	int depth;
2900aa77f6cSMauro Carvalho Chehab };
2910aa77f6cSMauro Carvalho Chehab 
2920aa77f6cSMauro Carvalho Chehab /* buffer for one video frame */
2930aa77f6cSMauro Carvalho Chehab struct s2255_buffer {
2940aa77f6cSMauro Carvalho Chehab 	/* common v4l buffer stuff -- must be first */
2950aa77f6cSMauro Carvalho Chehab 	struct videobuf_buffer vb;
2960aa77f6cSMauro Carvalho Chehab };
2970aa77f6cSMauro Carvalho Chehab 
2980aa77f6cSMauro Carvalho Chehab struct s2255_fh {
29944d06d82SHans Verkuil 	/* this must be the first field in this struct */
30044d06d82SHans Verkuil 	struct v4l2_fh		fh;
3010aa77f6cSMauro Carvalho Chehab 	struct videobuf_queue	vb_vidq;
3025e950fafSDean Anderson 	struct s2255_vc	*vc;
3030aa77f6cSMauro Carvalho Chehab 	int			resources;
3040aa77f6cSMauro Carvalho Chehab };
3050aa77f6cSMauro Carvalho Chehab 
3060aa77f6cSMauro Carvalho Chehab /* current cypress EEPROM firmware version */
3070aa77f6cSMauro Carvalho Chehab #define S2255_CUR_USB_FWVER	((3 << 8) | 12)
3080aa77f6cSMauro Carvalho Chehab /* current DSP FW version */
3090aa77f6cSMauro Carvalho Chehab #define S2255_CUR_DSP_FWVER     10104
3100aa77f6cSMauro Carvalho Chehab /* Need DSP version 5+ for video status feature */
3110aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_STATUS      5
3120aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_COLORFILTER 8
313469af77aSHans Verkuil #define S2255_NORMS		(V4L2_STD_ALL)
3140aa77f6cSMauro Carvalho Chehab 
3150aa77f6cSMauro Carvalho Chehab /* private V4L2 controls */
3160aa77f6cSMauro Carvalho Chehab 
3170aa77f6cSMauro Carvalho Chehab /*
3180aa77f6cSMauro Carvalho Chehab  * The following chart displays how COLORFILTER should be set
3190aa77f6cSMauro Carvalho Chehab  *  =========================================================
3200aa77f6cSMauro Carvalho Chehab  *  =     fourcc              =     COLORFILTER             =
3210aa77f6cSMauro Carvalho Chehab  *  =                         ===============================
3220aa77f6cSMauro Carvalho Chehab  *  =                         =   0             =    1      =
3230aa77f6cSMauro Carvalho Chehab  *  =========================================================
3240aa77f6cSMauro Carvalho Chehab  *  =  V4L2_PIX_FMT_GREY(Y8)  = monochrome from = monochrome=
3250aa77f6cSMauro Carvalho Chehab  *  =                         = s-video or      = composite =
3260aa77f6cSMauro Carvalho Chehab  *  =                         = B/W camera      = input     =
3270aa77f6cSMauro Carvalho Chehab  *  =========================================================
3280aa77f6cSMauro Carvalho Chehab  *  =    other                = color, svideo   = color,    =
3290aa77f6cSMauro Carvalho Chehab  *  =                         =                 = composite =
3300aa77f6cSMauro Carvalho Chehab  *  =========================================================
3310aa77f6cSMauro Carvalho Chehab  *
3320aa77f6cSMauro Carvalho Chehab  * Notes:
3330aa77f6cSMauro Carvalho Chehab  *   channels 0-3 on 2255 are composite
3340aa77f6cSMauro Carvalho Chehab  *   channels 0-1 on 2257 are composite, 2-3 are s-video
3350aa77f6cSMauro Carvalho Chehab  * If COLORFILTER is 0 with a composite color camera connected,
3360aa77f6cSMauro Carvalho Chehab  * the output will appear monochrome but hatching
3370aa77f6cSMauro Carvalho Chehab  * will occur.
3380aa77f6cSMauro Carvalho Chehab  * COLORFILTER is different from "color killer" and "color effects"
3390aa77f6cSMauro Carvalho Chehab  * for reasons above.
3400aa77f6cSMauro Carvalho Chehab  */
3410aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_ON  1
3420aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_OFF 0
343192f1e78SHans Verkuil #define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0)
3440aa77f6cSMauro Carvalho Chehab 
3450aa77f6cSMauro Carvalho Chehab /* frame prefix size (sent once every frame) */
3460aa77f6cSMauro Carvalho Chehab #define PREFIX_SIZE		512
3470aa77f6cSMauro Carvalho Chehab 
3480aa77f6cSMauro Carvalho Chehab /* Channels on box are in reverse order */
3490aa77f6cSMauro Carvalho Chehab static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
3500aa77f6cSMauro Carvalho Chehab 
3510aa77f6cSMauro Carvalho Chehab static int debug;
3520aa77f6cSMauro Carvalho Chehab 
3530aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev);
3540aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev);
3555e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc);
3565e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc);
3575e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf,
3580aa77f6cSMauro Carvalho Chehab 			   int jpgsize);
3595e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode);
3600aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev);
3610aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset);
3620aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev);
3630aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
3640aa77f6cSMauro Carvalho Chehab 			     u16 index, u16 value, void *buf,
3650aa77f6cSMauro Carvalho Chehab 			     s32 buf_len, int bOut);
3660aa77f6cSMauro Carvalho Chehab 
3670aa77f6cSMauro Carvalho Chehab /* dev_err macro with driver name */
3680aa77f6cSMauro Carvalho Chehab #define S2255_DRIVER_NAME "s2255"
3690aa77f6cSMauro Carvalho Chehab #define s2255_dev_err(dev, fmt, arg...)					\
3700aa77f6cSMauro Carvalho Chehab 		dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
3710aa77f6cSMauro Carvalho Chehab 
372f5402007Ssensoray-dev #define dprintk(dev, level, fmt, arg...) \
373f5402007Ssensoray-dev 	v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
3740aa77f6cSMauro Carvalho Chehab 
3750aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver;
3760aa77f6cSMauro Carvalho Chehab 
3770aa77f6cSMauro Carvalho Chehab /* start video number */
3780aa77f6cSMauro Carvalho Chehab static int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
3790aa77f6cSMauro Carvalho Chehab 
3800aa77f6cSMauro Carvalho Chehab /* Enable jpeg capture. */
3810aa77f6cSMauro Carvalho Chehab static int jpeg_enable = 1;
3820aa77f6cSMauro Carvalho Chehab 
3830aa77f6cSMauro Carvalho Chehab module_param(debug, int, 0644);
3840aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
3850aa77f6cSMauro Carvalho Chehab module_param(video_nr, int, 0644);
3860aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
3870aa77f6cSMauro Carvalho Chehab module_param(jpeg_enable, int, 0644);
3880aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
3890aa77f6cSMauro Carvalho Chehab 
3900aa77f6cSMauro Carvalho Chehab /* USB device table */
3910aa77f6cSMauro Carvalho Chehab #define USB_SENSORAY_VID	0x1943
3920aa77f6cSMauro Carvalho Chehab static struct usb_device_id s2255_table[] = {
3930aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
3940aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
3950aa77f6cSMauro Carvalho Chehab 	{ }			/* Terminating entry */
3960aa77f6cSMauro Carvalho Chehab };
3970aa77f6cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, s2255_table);
3980aa77f6cSMauro Carvalho Chehab 
3990aa77f6cSMauro Carvalho Chehab #define BUFFER_TIMEOUT msecs_to_jiffies(400)
4000aa77f6cSMauro Carvalho Chehab 
4010aa77f6cSMauro Carvalho Chehab /* image formats.  */
4020aa77f6cSMauro Carvalho Chehab /* JPEG formats must be defined last to support jpeg_enable parameter */
4030aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt formats[] = {
4040aa77f6cSMauro Carvalho Chehab 	{
4050aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, packed, YUYV",
4060aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_YUYV,
4070aa77f6cSMauro Carvalho Chehab 		.depth = 16
4080aa77f6cSMauro Carvalho Chehab 
4090aa77f6cSMauro Carvalho Chehab 	}, {
4100aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, packed, UYVY",
4110aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_UYVY,
4120aa77f6cSMauro Carvalho Chehab 		.depth = 16
4130aa77f6cSMauro Carvalho Chehab 	}, {
4145c632b22SHans Verkuil 		.name = "4:2:2, planar, YUV422P",
4155c632b22SHans Verkuil 		.fourcc = V4L2_PIX_FMT_YUV422P,
4165c632b22SHans Verkuil 		.depth = 16
4175c632b22SHans Verkuil 
4185c632b22SHans Verkuil 	}, {
4190aa77f6cSMauro Carvalho Chehab 		.name = "8bpp GREY",
4200aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_GREY,
4210aa77f6cSMauro Carvalho Chehab 		.depth = 8
4220aa77f6cSMauro Carvalho Chehab 	}, {
4230aa77f6cSMauro Carvalho Chehab 		.name = "JPG",
4240aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_JPEG,
4250aa77f6cSMauro Carvalho Chehab 		.depth = 24
4260aa77f6cSMauro Carvalho Chehab 	}, {
4270aa77f6cSMauro Carvalho Chehab 		.name = "MJPG",
4280aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_MJPEG,
4290aa77f6cSMauro Carvalho Chehab 		.depth = 24
4300aa77f6cSMauro Carvalho Chehab 	}
4310aa77f6cSMauro Carvalho Chehab };
4320aa77f6cSMauro Carvalho Chehab 
4335e950fafSDean Anderson static int norm_maxw(struct s2255_vc *vc)
4340aa77f6cSMauro Carvalho Chehab {
4355e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4360aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
4370aa77f6cSMauro Carvalho Chehab }
4380aa77f6cSMauro Carvalho Chehab 
4395e950fafSDean Anderson static int norm_maxh(struct s2255_vc *vc)
4400aa77f6cSMauro Carvalho Chehab {
4415e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4420aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
4430aa77f6cSMauro Carvalho Chehab }
4440aa77f6cSMauro Carvalho Chehab 
4455e950fafSDean Anderson static int norm_minw(struct s2255_vc *vc)
4460aa77f6cSMauro Carvalho Chehab {
4475e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4480aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
4490aa77f6cSMauro Carvalho Chehab }
4500aa77f6cSMauro Carvalho Chehab 
4515e950fafSDean Anderson static int norm_minh(struct s2255_vc *vc)
4520aa77f6cSMauro Carvalho Chehab {
4535e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4540aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
4550aa77f6cSMauro Carvalho Chehab }
4560aa77f6cSMauro Carvalho Chehab 
4570aa77f6cSMauro Carvalho Chehab 
4580aa77f6cSMauro Carvalho Chehab /*
4590aa77f6cSMauro Carvalho Chehab  * TODO: fixme: move YUV reordering to hardware
4600aa77f6cSMauro Carvalho Chehab  * converts 2255 planar format to yuyv or uyvy
4610aa77f6cSMauro Carvalho Chehab  */
4620aa77f6cSMauro Carvalho Chehab static void planar422p_to_yuv_packed(const unsigned char *in,
4630aa77f6cSMauro Carvalho Chehab 				     unsigned char *out,
4640aa77f6cSMauro Carvalho Chehab 				     int width, int height,
4650aa77f6cSMauro Carvalho Chehab 				     int fmt)
4660aa77f6cSMauro Carvalho Chehab {
4670aa77f6cSMauro Carvalho Chehab 	unsigned char *pY;
4680aa77f6cSMauro Carvalho Chehab 	unsigned char *pCb;
4690aa77f6cSMauro Carvalho Chehab 	unsigned char *pCr;
4700aa77f6cSMauro Carvalho Chehab 	unsigned long size = height * width;
4710aa77f6cSMauro Carvalho Chehab 	unsigned int i;
4720aa77f6cSMauro Carvalho Chehab 	pY = (unsigned char *)in;
4730aa77f6cSMauro Carvalho Chehab 	pCr = (unsigned char *)in + height * width;
4740aa77f6cSMauro Carvalho Chehab 	pCb = (unsigned char *)in + height * width + (height * width / 2);
4750aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < size * 2; i += 4) {
4760aa77f6cSMauro Carvalho Chehab 		out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
4770aa77f6cSMauro Carvalho Chehab 		out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
4780aa77f6cSMauro Carvalho Chehab 		out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
4790aa77f6cSMauro Carvalho Chehab 		out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
4800aa77f6cSMauro Carvalho Chehab 	}
4810aa77f6cSMauro Carvalho Chehab 	return;
4820aa77f6cSMauro Carvalho Chehab }
4830aa77f6cSMauro Carvalho Chehab 
4840aa77f6cSMauro Carvalho Chehab static void s2255_reset_dsppower(struct s2255_dev *dev)
4850aa77f6cSMauro Carvalho Chehab {
4860aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
487f5402007Ssensoray-dev 	msleep(20);
4880aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
4890aa77f6cSMauro Carvalho Chehab 	msleep(600);
4900aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
4910aa77f6cSMauro Carvalho Chehab 	return;
4920aa77f6cSMauro Carvalho Chehab }
4930aa77f6cSMauro Carvalho Chehab 
4940aa77f6cSMauro Carvalho Chehab /* kickstarts the firmware loading. from probe
4950aa77f6cSMauro Carvalho Chehab  */
4960aa77f6cSMauro Carvalho Chehab static void s2255_timer(unsigned long user_data)
4970aa77f6cSMauro Carvalho Chehab {
4980aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = (struct s2255_fw *)user_data;
4990aa77f6cSMauro Carvalho Chehab 	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
500f5402007Ssensoray-dev 		pr_err("s2255: can't submit urb\n");
5010aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5020aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5030aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5040aa77f6cSMauro Carvalho Chehab 		return;
5050aa77f6cSMauro Carvalho Chehab 	}
5060aa77f6cSMauro Carvalho Chehab }
5070aa77f6cSMauro Carvalho Chehab 
5080aa77f6cSMauro Carvalho Chehab 
5090aa77f6cSMauro Carvalho Chehab /* this loads the firmware asynchronously.
5100b84caabSHans Verkuil    Originally this was done synchronously in probe.
5110aa77f6cSMauro Carvalho Chehab    But it is better to load it asynchronously here than block
5120aa77f6cSMauro Carvalho Chehab    inside the probe function. Blocking inside probe affects boot time.
5130aa77f6cSMauro Carvalho Chehab    FW loading is triggered by the timer in the probe function
5140aa77f6cSMauro Carvalho Chehab */
5150aa77f6cSMauro Carvalho Chehab static void s2255_fwchunk_complete(struct urb *urb)
5160aa77f6cSMauro Carvalho Chehab {
5170aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = urb->context;
5180aa77f6cSMauro Carvalho Chehab 	struct usb_device *udev = urb->dev;
5190aa77f6cSMauro Carvalho Chehab 	int len;
5200aa77f6cSMauro Carvalho Chehab 	if (urb->status) {
5210aa77f6cSMauro Carvalho Chehab 		dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
5220aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5230aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5240aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5250aa77f6cSMauro Carvalho Chehab 		return;
5260aa77f6cSMauro Carvalho Chehab 	}
5270aa77f6cSMauro Carvalho Chehab 	if (data->fw_urb == NULL) {
5280aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&udev->dev, "disconnected\n");
5290aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5300aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5310aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5320aa77f6cSMauro Carvalho Chehab 		return;
5330aa77f6cSMauro Carvalho Chehab 	}
5340aa77f6cSMauro Carvalho Chehab #define CHUNK_SIZE 512
5350aa77f6cSMauro Carvalho Chehab 	/* all USB transfers must be done with continuous kernel memory.
5360aa77f6cSMauro Carvalho Chehab 	   can't allocate more than 128k in current linux kernel, so
5370aa77f6cSMauro Carvalho Chehab 	   upload the firmware in chunks
5380aa77f6cSMauro Carvalho Chehab 	 */
5390aa77f6cSMauro Carvalho Chehab 	if (data->fw_loaded < data->fw_size) {
5400aa77f6cSMauro Carvalho Chehab 		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
5410aa77f6cSMauro Carvalho Chehab 		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
5420aa77f6cSMauro Carvalho Chehab 
5430aa77f6cSMauro Carvalho Chehab 		if (len < CHUNK_SIZE)
5440aa77f6cSMauro Carvalho Chehab 			memset(data->pfw_data, 0, CHUNK_SIZE);
5450aa77f6cSMauro Carvalho Chehab 
5460aa77f6cSMauro Carvalho Chehab 		memcpy(data->pfw_data,
5470aa77f6cSMauro Carvalho Chehab 		       (char *) data->fw->data + data->fw_loaded, len);
5480aa77f6cSMauro Carvalho Chehab 
5490aa77f6cSMauro Carvalho Chehab 		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
5500aa77f6cSMauro Carvalho Chehab 				  data->pfw_data, CHUNK_SIZE,
5510aa77f6cSMauro Carvalho Chehab 				  s2255_fwchunk_complete, data);
5520aa77f6cSMauro Carvalho Chehab 		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
5530aa77f6cSMauro Carvalho Chehab 			dev_err(&udev->dev, "failed submit URB\n");
5540aa77f6cSMauro Carvalho Chehab 			atomic_set(&data->fw_state, S2255_FW_FAILED);
5550aa77f6cSMauro Carvalho Chehab 			/* wake up anything waiting for the firmware */
5560aa77f6cSMauro Carvalho Chehab 			wake_up(&data->wait_fw);
5570aa77f6cSMauro Carvalho Chehab 			return;
5580aa77f6cSMauro Carvalho Chehab 		}
5590aa77f6cSMauro Carvalho Chehab 		data->fw_loaded += len;
560f5402007Ssensoray-dev 	} else
5610aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
5620aa77f6cSMauro Carvalho Chehab 	return;
5630aa77f6cSMauro Carvalho Chehab 
5640aa77f6cSMauro Carvalho Chehab }
5650aa77f6cSMauro Carvalho Chehab 
5665e950fafSDean Anderson static int s2255_got_frame(struct s2255_vc *vc, int jpgsize)
5670aa77f6cSMauro Carvalho Chehab {
5680aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf;
5695e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
5700aa77f6cSMauro Carvalho Chehab 	unsigned long flags = 0;
5710aa77f6cSMauro Carvalho Chehab 	int rc = 0;
5720aa77f6cSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->slock, flags);
5735e950fafSDean Anderson 	if (list_empty(&vc->buf_list)) {
574f5402007Ssensoray-dev 		dprintk(dev, 1, "No active queue to serve\n");
5750aa77f6cSMauro Carvalho Chehab 		rc = -1;
5760aa77f6cSMauro Carvalho Chehab 		goto unlock;
5770aa77f6cSMauro Carvalho Chehab 	}
5785e950fafSDean Anderson 	buf = list_entry(vc->buf_list.next,
5790aa77f6cSMauro Carvalho Chehab 			 struct s2255_buffer, vb.queue);
5800aa77f6cSMauro Carvalho Chehab 	list_del(&buf->vb.queue);
5818e6057b5SSakari Ailus 	v4l2_get_timestamp(&buf->vb.ts);
5825e950fafSDean Anderson 	s2255_fillbuff(vc, buf, jpgsize);
5830aa77f6cSMauro Carvalho Chehab 	wake_up(&buf->vb.done);
584f5402007Ssensoray-dev 	dprintk(dev, 2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
5850aa77f6cSMauro Carvalho Chehab unlock:
5860aa77f6cSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->slock, flags);
5870aa77f6cSMauro Carvalho Chehab 	return rc;
5880aa77f6cSMauro Carvalho Chehab }
5890aa77f6cSMauro Carvalho Chehab 
5900aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt *format_by_fourcc(int fourcc)
5910aa77f6cSMauro Carvalho Chehab {
5920aa77f6cSMauro Carvalho Chehab 	unsigned int i;
5930aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
5940aa77f6cSMauro Carvalho Chehab 		if (-1 == formats[i].fourcc)
5950aa77f6cSMauro Carvalho Chehab 			continue;
5960aa77f6cSMauro Carvalho Chehab 		if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
5970aa77f6cSMauro Carvalho Chehab 				     (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
5980aa77f6cSMauro Carvalho Chehab 			continue;
5990aa77f6cSMauro Carvalho Chehab 		if (formats[i].fourcc == fourcc)
6000aa77f6cSMauro Carvalho Chehab 			return formats + i;
6010aa77f6cSMauro Carvalho Chehab 	}
6020aa77f6cSMauro Carvalho Chehab 	return NULL;
6030aa77f6cSMauro Carvalho Chehab }
6040aa77f6cSMauro Carvalho Chehab 
6050aa77f6cSMauro Carvalho Chehab /* video buffer vmalloc implementation based partly on VIVI driver which is
6060aa77f6cSMauro Carvalho Chehab  *          Copyright (c) 2006 by
6070aa77f6cSMauro Carvalho Chehab  *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
6080aa77f6cSMauro Carvalho Chehab  *                  Ted Walther <ted--a.t--enumera.com>
6090aa77f6cSMauro Carvalho Chehab  *                  John Sokol <sokol--a.t--videotechnology.com>
6100aa77f6cSMauro Carvalho Chehab  *                  http://v4l.videotechnology.com/
6110aa77f6cSMauro Carvalho Chehab  *
6120aa77f6cSMauro Carvalho Chehab  */
6135e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc,
6140aa77f6cSMauro Carvalho Chehab 			   struct s2255_buffer *buf, int jpgsize)
6150aa77f6cSMauro Carvalho Chehab {
6160aa77f6cSMauro Carvalho Chehab 	int pos = 0;
6170aa77f6cSMauro Carvalho Chehab 	const char *tmpbuf;
6180aa77f6cSMauro Carvalho Chehab 	char *vbuf = videobuf_to_vmalloc(&buf->vb);
6190aa77f6cSMauro Carvalho Chehab 	unsigned long last_frame;
6205e950fafSDean Anderson 	struct s2255_dev *dev = vc->dev;
6210aa77f6cSMauro Carvalho Chehab 
6220aa77f6cSMauro Carvalho Chehab 	if (!vbuf)
6230aa77f6cSMauro Carvalho Chehab 		return;
6245e950fafSDean Anderson 	last_frame = vc->last_frame;
6250aa77f6cSMauro Carvalho Chehab 	if (last_frame != -1) {
6260aa77f6cSMauro Carvalho Chehab 		tmpbuf =
6275e950fafSDean Anderson 		    (const char *)vc->buffer.frame[last_frame].lpvbits;
6288bf405a0SDean Anderson 		switch (vc->fmt->fourcc) {
6290aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUYV:
6300aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_UYVY:
6310aa77f6cSMauro Carvalho Chehab 			planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
6320aa77f6cSMauro Carvalho Chehab 						 vbuf, buf->vb.width,
6330aa77f6cSMauro Carvalho Chehab 						 buf->vb.height,
6348bf405a0SDean Anderson 						 vc->fmt->fourcc);
6350aa77f6cSMauro Carvalho Chehab 			break;
6360aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_GREY:
6370aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
6380aa77f6cSMauro Carvalho Chehab 			break;
6390aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_JPEG:
6400aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_MJPEG:
6410aa77f6cSMauro Carvalho Chehab 			buf->vb.size = jpgsize;
6420aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf, buf->vb.size);
6430aa77f6cSMauro Carvalho Chehab 			break;
6440aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUV422P:
6450aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf,
6460aa77f6cSMauro Carvalho Chehab 			       buf->vb.width * buf->vb.height * 2);
6470aa77f6cSMauro Carvalho Chehab 			break;
6480aa77f6cSMauro Carvalho Chehab 		default:
649f5402007Ssensoray-dev 			pr_info("s2255: unknown format?\n");
6500aa77f6cSMauro Carvalho Chehab 		}
6515e950fafSDean Anderson 		vc->last_frame = -1;
6520aa77f6cSMauro Carvalho Chehab 	} else {
653f5402007Ssensoray-dev 		pr_err("s2255: =======no frame\n");
6540aa77f6cSMauro Carvalho Chehab 		return;
6550aa77f6cSMauro Carvalho Chehab 	}
656f5402007Ssensoray-dev 	dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n",
6570aa77f6cSMauro Carvalho Chehab 		(unsigned long)vbuf, pos);
6580aa77f6cSMauro Carvalho Chehab 	/* tell v4l buffer was filled */
6595e950fafSDean Anderson 	buf->vb.field_count = vc->frame_count * 2;
6608e6057b5SSakari Ailus 	v4l2_get_timestamp(&buf->vb.ts);
6610aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_DONE;
6620aa77f6cSMauro Carvalho Chehab }
6630aa77f6cSMauro Carvalho Chehab 
6640aa77f6cSMauro Carvalho Chehab 
6650aa77f6cSMauro Carvalho Chehab /* ------------------------------------------------------------------
6660aa77f6cSMauro Carvalho Chehab    Videobuf operations
6670aa77f6cSMauro Carvalho Chehab    ------------------------------------------------------------------*/
6680aa77f6cSMauro Carvalho Chehab 
6699da62eb0SDean Anderson static int buffer_setup(struct videobuf_queue *vq, unsigned int *nbuffers,
6700aa77f6cSMauro Carvalho Chehab 			unsigned int *size)
6710aa77f6cSMauro Carvalho Chehab {
6720aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
6735e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
674*92cde477SDean Anderson 
6755e950fafSDean Anderson 	*size = vc->width * vc->height * (vc->fmt->depth >> 3);
6760aa77f6cSMauro Carvalho Chehab 
6779da62eb0SDean Anderson 	if (*nbuffers < S2255_MIN_BUFS)
6789da62eb0SDean Anderson 		*nbuffers = S2255_MIN_BUFS;
6790aa77f6cSMauro Carvalho Chehab 
6800aa77f6cSMauro Carvalho Chehab 	return 0;
6810aa77f6cSMauro Carvalho Chehab }
6820aa77f6cSMauro Carvalho Chehab 
6830aa77f6cSMauro Carvalho Chehab static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
6840aa77f6cSMauro Carvalho Chehab {
6850aa77f6cSMauro Carvalho Chehab 	videobuf_vmalloc_free(&buf->vb);
6860aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_NEEDS_INIT;
6870aa77f6cSMauro Carvalho Chehab }
6880aa77f6cSMauro Carvalho Chehab 
6890aa77f6cSMauro Carvalho Chehab static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
6900aa77f6cSMauro Carvalho Chehab 			  enum v4l2_field field)
6910aa77f6cSMauro Carvalho Chehab {
6920aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
6935e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
6940aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
6950aa77f6cSMauro Carvalho Chehab 	int rc;
6965e950fafSDean Anderson 	int w = vc->width;
6975e950fafSDean Anderson 	int h = vc->height;
698*92cde477SDean Anderson 	dprintk(vc->dev, 4, "%s, field=%d\n", __func__, field);
6995e950fafSDean Anderson 	if (vc->fmt == NULL)
7000aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7010aa77f6cSMauro Carvalho Chehab 
7025e950fafSDean Anderson 	if ((w < norm_minw(vc)) ||
7035e950fafSDean Anderson 	    (w > norm_maxw(vc)) ||
7045e950fafSDean Anderson 	    (h < norm_minh(vc)) ||
7055e950fafSDean Anderson 	    (h > norm_maxh(vc))) {
706*92cde477SDean Anderson 		dprintk(vc->dev, 4, "invalid buffer prepare\n");
7070aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7080aa77f6cSMauro Carvalho Chehab 	}
7095e950fafSDean Anderson 	buf->vb.size = w * h * (vc->fmt->depth >> 3);
7100aa77f6cSMauro Carvalho Chehab 	if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
711*92cde477SDean Anderson 		dprintk(vc->dev, 4, "invalid buffer prepare\n");
7120aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7130aa77f6cSMauro Carvalho Chehab 	}
7140aa77f6cSMauro Carvalho Chehab 
7150aa77f6cSMauro Carvalho Chehab 	buf->vb.width = w;
7160aa77f6cSMauro Carvalho Chehab 	buf->vb.height = h;
7170aa77f6cSMauro Carvalho Chehab 	buf->vb.field = field;
7180aa77f6cSMauro Carvalho Chehab 
7190aa77f6cSMauro Carvalho Chehab 	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
7200aa77f6cSMauro Carvalho Chehab 		rc = videobuf_iolock(vq, &buf->vb, NULL);
7210aa77f6cSMauro Carvalho Chehab 		if (rc < 0)
7220aa77f6cSMauro Carvalho Chehab 			goto fail;
7230aa77f6cSMauro Carvalho Chehab 	}
7240aa77f6cSMauro Carvalho Chehab 
7250aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_PREPARED;
7260aa77f6cSMauro Carvalho Chehab 	return 0;
7270aa77f6cSMauro Carvalho Chehab fail:
7280aa77f6cSMauro Carvalho Chehab 	free_buffer(vq, buf);
7290aa77f6cSMauro Carvalho Chehab 	return rc;
7300aa77f6cSMauro Carvalho Chehab }
7310aa77f6cSMauro Carvalho Chehab 
7320aa77f6cSMauro Carvalho Chehab static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
7330aa77f6cSMauro Carvalho Chehab {
7340aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
7350aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
7365e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
737*92cde477SDean Anderson 	dprintk(vc->dev, 1, "%s\n", __func__);
7380aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_QUEUED;
7395e950fafSDean Anderson 	list_add_tail(&buf->vb.queue, &vc->buf_list);
7400aa77f6cSMauro Carvalho Chehab }
7410aa77f6cSMauro Carvalho Chehab 
7420aa77f6cSMauro Carvalho Chehab static void buffer_release(struct videobuf_queue *vq,
7430aa77f6cSMauro Carvalho Chehab 			   struct videobuf_buffer *vb)
7440aa77f6cSMauro Carvalho Chehab {
7450aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
7460aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
747*92cde477SDean Anderson 	struct s2255_vc *vc = fh->vc;
748*92cde477SDean Anderson 	dprintk(vc->dev, 4, "%s %d\n", __func__, vc->idx);
7490aa77f6cSMauro Carvalho Chehab 	free_buffer(vq, buf);
7500aa77f6cSMauro Carvalho Chehab }
7510aa77f6cSMauro Carvalho Chehab 
7520aa77f6cSMauro Carvalho Chehab static struct videobuf_queue_ops s2255_video_qops = {
7530aa77f6cSMauro Carvalho Chehab 	.buf_setup = buffer_setup,
7540aa77f6cSMauro Carvalho Chehab 	.buf_prepare = buffer_prepare,
7550aa77f6cSMauro Carvalho Chehab 	.buf_queue = buffer_queue,
7560aa77f6cSMauro Carvalho Chehab 	.buf_release = buffer_release,
7570aa77f6cSMauro Carvalho Chehab };
7580aa77f6cSMauro Carvalho Chehab 
7590aa77f6cSMauro Carvalho Chehab 
7600aa77f6cSMauro Carvalho Chehab static int res_get(struct s2255_fh *fh)
7610aa77f6cSMauro Carvalho Chehab {
7625e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
7630aa77f6cSMauro Carvalho Chehab 	/* is it free? */
7645e950fafSDean Anderson 	if (vc->resources)
7650aa77f6cSMauro Carvalho Chehab 		return 0; /* no, someone else uses it */
7660aa77f6cSMauro Carvalho Chehab 	/* it's free, grab it */
7675e950fafSDean Anderson 	vc->resources = 1;
7680aa77f6cSMauro Carvalho Chehab 	fh->resources = 1;
769*92cde477SDean Anderson 	dprintk(vc->dev, 1, "s2255: res: get\n");
7700aa77f6cSMauro Carvalho Chehab 	return 1;
7710aa77f6cSMauro Carvalho Chehab }
7720aa77f6cSMauro Carvalho Chehab 
7730aa77f6cSMauro Carvalho Chehab static int res_locked(struct s2255_fh *fh)
7740aa77f6cSMauro Carvalho Chehab {
7755e950fafSDean Anderson 	return fh->vc->resources;
7760aa77f6cSMauro Carvalho Chehab }
7770aa77f6cSMauro Carvalho Chehab 
7780aa77f6cSMauro Carvalho Chehab static int res_check(struct s2255_fh *fh)
7790aa77f6cSMauro Carvalho Chehab {
7800aa77f6cSMauro Carvalho Chehab 	return fh->resources;
7810aa77f6cSMauro Carvalho Chehab }
7820aa77f6cSMauro Carvalho Chehab 
7830aa77f6cSMauro Carvalho Chehab 
7840aa77f6cSMauro Carvalho Chehab static void res_free(struct s2255_fh *fh)
7850aa77f6cSMauro Carvalho Chehab {
7865e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
7875e950fafSDean Anderson 	vc->resources = 0;
7880aa77f6cSMauro Carvalho Chehab 	fh->resources = 0;
7890aa77f6cSMauro Carvalho Chehab }
7900aa77f6cSMauro Carvalho Chehab 
7910aa77f6cSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv,
7920aa77f6cSMauro Carvalho Chehab 			   struct v4l2_capability *cap)
7930aa77f6cSMauro Carvalho Chehab {
7940aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
795*92cde477SDean Anderson 	struct s2255_dev *dev = fh->vc->dev;
79639696009SHans Verkuil 
7970aa77f6cSMauro Carvalho Chehab 	strlcpy(cap->driver, "s2255", sizeof(cap->driver));
7980aa77f6cSMauro Carvalho Chehab 	strlcpy(cap->card, "s2255", sizeof(cap->card));
7990aa77f6cSMauro Carvalho Chehab 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
80039696009SHans Verkuil 	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
80139696009SHans Verkuil 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
8020aa77f6cSMauro Carvalho Chehab 	return 0;
8030aa77f6cSMauro Carvalho Chehab }
8040aa77f6cSMauro Carvalho Chehab 
8050aa77f6cSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
8060aa77f6cSMauro Carvalho Chehab 			       struct v4l2_fmtdesc *f)
8070aa77f6cSMauro Carvalho Chehab {
8080aa77f6cSMauro Carvalho Chehab 	int index = f->index;
8090aa77f6cSMauro Carvalho Chehab 
8100aa77f6cSMauro Carvalho Chehab 	if (index >= ARRAY_SIZE(formats))
8110aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8120aa77f6cSMauro Carvalho Chehab 	if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
8130aa77f6cSMauro Carvalho Chehab 			(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
8140aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8150aa77f6cSMauro Carvalho Chehab 	strlcpy(f->description, formats[index].name, sizeof(f->description));
8160aa77f6cSMauro Carvalho Chehab 	f->pixelformat = formats[index].fourcc;
8170aa77f6cSMauro Carvalho Chehab 	return 0;
8180aa77f6cSMauro Carvalho Chehab }
8190aa77f6cSMauro Carvalho Chehab 
8200aa77f6cSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
8210aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
8220aa77f6cSMauro Carvalho Chehab {
8230aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
8245e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
8255e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
8260aa77f6cSMauro Carvalho Chehab 
8275e950fafSDean Anderson 	f->fmt.pix.width = vc->width;
8285e950fafSDean Anderson 	f->fmt.pix.height = vc->height;
82992513611SHans Verkuil 	if (f->fmt.pix.height >=
83092513611SHans Verkuil 	    (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2)
83192513611SHans Verkuil 		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
83292513611SHans Verkuil 	else
83392513611SHans Verkuil 		f->fmt.pix.field = V4L2_FIELD_TOP;
8345e950fafSDean Anderson 	f->fmt.pix.pixelformat = vc->fmt->fourcc;
8355e950fafSDean Anderson 	f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3);
8360aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
83729ceb110SHans Verkuil 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
83829ceb110SHans Verkuil 	f->fmt.pix.priv = 0;
8390aa77f6cSMauro Carvalho Chehab 	return 0;
8400aa77f6cSMauro Carvalho Chehab }
8410aa77f6cSMauro Carvalho Chehab 
8420aa77f6cSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
8430aa77f6cSMauro Carvalho Chehab 			      struct v4l2_format *f)
8440aa77f6cSMauro Carvalho Chehab {
8450aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
8460aa77f6cSMauro Carvalho Chehab 	enum v4l2_field field;
8470aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
8485e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
8495e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
8500aa77f6cSMauro Carvalho Chehab 
8510aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
8520aa77f6cSMauro Carvalho Chehab 
8530aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
8540aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8550aa77f6cSMauro Carvalho Chehab 
8560aa77f6cSMauro Carvalho Chehab 	field = f->fmt.pix.field;
8570aa77f6cSMauro Carvalho Chehab 
858*92cde477SDean Anderson 	dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n",
8590aa77f6cSMauro Carvalho Chehab 		__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
8600aa77f6cSMauro Carvalho Chehab 	if (is_ntsc) {
8610aa77f6cSMauro Carvalho Chehab 		/* NTSC */
8620aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
8630aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
86492513611SHans Verkuil 			field = V4L2_FIELD_INTERLACED;
8650aa77f6cSMauro Carvalho Chehab 		} else {
8660aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
8670aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
8680aa77f6cSMauro Carvalho Chehab 		}
8690aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
8700aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
8710aa77f6cSMauro Carvalho Chehab 		else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
8720aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
8730aa77f6cSMauro Carvalho Chehab 		else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
8740aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
8750aa77f6cSMauro Carvalho Chehab 		else
8760aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
8770aa77f6cSMauro Carvalho Chehab 	} else {
8780aa77f6cSMauro Carvalho Chehab 		/* PAL */
8790aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
8800aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
88192513611SHans Verkuil 			field = V4L2_FIELD_INTERLACED;
8820aa77f6cSMauro Carvalho Chehab 		} else {
8830aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
8840aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
8850aa77f6cSMauro Carvalho Chehab 		}
88692513611SHans Verkuil 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL)
8870aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
88892513611SHans Verkuil 		else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL)
8890aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
89092513611SHans Verkuil 		else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL)
8910aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
89292513611SHans Verkuil 		else
8930aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
8940aa77f6cSMauro Carvalho Chehab 	}
8950aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.field = field;
8960aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
8970aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
89829ceb110SHans Verkuil 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
89929ceb110SHans Verkuil 	f->fmt.pix.priv = 0;
900*92cde477SDean Anderson 	dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__,
9010aa77f6cSMauro Carvalho Chehab 		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
9020aa77f6cSMauro Carvalho Chehab 	return 0;
9030aa77f6cSMauro Carvalho Chehab }
9040aa77f6cSMauro Carvalho Chehab 
9050aa77f6cSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
9060aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
9070aa77f6cSMauro Carvalho Chehab {
9080aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
9095e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
9100aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
9110aa77f6cSMauro Carvalho Chehab 	struct videobuf_queue *q = &fh->vb_vidq;
9120aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
9130aa77f6cSMauro Carvalho Chehab 	int ret;
9140aa77f6cSMauro Carvalho Chehab 
9150aa77f6cSMauro Carvalho Chehab 	ret = vidioc_try_fmt_vid_cap(file, fh, f);
9160aa77f6cSMauro Carvalho Chehab 
9170aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
9180aa77f6cSMauro Carvalho Chehab 		return ret;
9190aa77f6cSMauro Carvalho Chehab 
9200aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
9210aa77f6cSMauro Carvalho Chehab 
9220aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
9230aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
9240aa77f6cSMauro Carvalho Chehab 
9250aa77f6cSMauro Carvalho Chehab 	mutex_lock(&q->vb_lock);
9260aa77f6cSMauro Carvalho Chehab 
9270aa77f6cSMauro Carvalho Chehab 	if (videobuf_queue_is_busy(&fh->vb_vidq)) {
928*92cde477SDean Anderson 		dprintk(vc->dev, 1, "queue busy\n");
9290aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
9300aa77f6cSMauro Carvalho Chehab 		goto out_s_fmt;
9310aa77f6cSMauro Carvalho Chehab 	}
9320aa77f6cSMauro Carvalho Chehab 
9330aa77f6cSMauro Carvalho Chehab 	if (res_locked(fh)) {
934*92cde477SDean Anderson 		dprintk(vc->dev, 1, "%s: channel busy\n", __func__);
9350aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
9360aa77f6cSMauro Carvalho Chehab 		goto out_s_fmt;
9370aa77f6cSMauro Carvalho Chehab 	}
9385e950fafSDean Anderson 	mode = vc->mode;
9395e950fafSDean Anderson 	vc->fmt = fmt;
9405e950fafSDean Anderson 	vc->width = f->fmt.pix.width;
9415e950fafSDean Anderson 	vc->height = f->fmt.pix.height;
9420aa77f6cSMauro Carvalho Chehab 	fh->vb_vidq.field = f->fmt.pix.field;
9435e950fafSDean Anderson 	if (vc->width > norm_minw(vc)) {
9445e950fafSDean Anderson 		if (vc->height > norm_minh(vc)) {
9455e950fafSDean Anderson 			if (vc->cap_parm.capturemode &
9460aa77f6cSMauro Carvalho Chehab 			    V4L2_MODE_HIGHQUALITY)
9470aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFSI;
9480aa77f6cSMauro Carvalho Chehab 			else
9490aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFS;
9500aa77f6cSMauro Carvalho Chehab 		} else
9510aa77f6cSMauro Carvalho Chehab 			mode.scale = SCALE_2CIFS;
9520aa77f6cSMauro Carvalho Chehab 
9530aa77f6cSMauro Carvalho Chehab 	} else {
9540aa77f6cSMauro Carvalho Chehab 		mode.scale = SCALE_1CIFS;
9550aa77f6cSMauro Carvalho Chehab 	}
9560aa77f6cSMauro Carvalho Chehab 	/* color mode */
9575e950fafSDean Anderson 	switch (vc->fmt->fourcc) {
9580aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_GREY:
9590aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
9600aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_Y8;
9610aa77f6cSMauro Carvalho Chehab 		break;
9620aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_JPEG:
9630aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_MJPEG:
9640aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
9650aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_JPG;
9665e950fafSDean Anderson 		mode.color |= (vc->jpegqual << 8);
9670aa77f6cSMauro Carvalho Chehab 		break;
9680aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUV422P:
9690aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
9700aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPL;
9710aa77f6cSMauro Carvalho Chehab 		break;
9720aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUYV:
9730aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_UYVY:
9740aa77f6cSMauro Carvalho Chehab 	default:
9750aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
9760aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPK;
9770aa77f6cSMauro Carvalho Chehab 		break;
9780aa77f6cSMauro Carvalho Chehab 	}
9795e950fafSDean Anderson 	if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR))
9800aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
9815e950fafSDean Anderson 	else if (mode.scale != vc->mode.scale)
9820aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
9835e950fafSDean Anderson 	else if (mode.format != vc->mode.format)
9840aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
9855e950fafSDean Anderson 	vc->mode = mode;
9865e950fafSDean Anderson 	(void) s2255_set_mode(vc, &mode);
9870aa77f6cSMauro Carvalho Chehab 	ret = 0;
9880aa77f6cSMauro Carvalho Chehab out_s_fmt:
9890aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&q->vb_lock);
9900aa77f6cSMauro Carvalho Chehab 	return ret;
9910aa77f6cSMauro Carvalho Chehab }
9920aa77f6cSMauro Carvalho Chehab 
9930aa77f6cSMauro Carvalho Chehab static int vidioc_reqbufs(struct file *file, void *priv,
9940aa77f6cSMauro Carvalho Chehab 			  struct v4l2_requestbuffers *p)
9950aa77f6cSMauro Carvalho Chehab {
9960aa77f6cSMauro Carvalho Chehab 	int rc;
9970aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
9980aa77f6cSMauro Carvalho Chehab 	rc = videobuf_reqbufs(&fh->vb_vidq, p);
9990aa77f6cSMauro Carvalho Chehab 	return rc;
10000aa77f6cSMauro Carvalho Chehab }
10010aa77f6cSMauro Carvalho Chehab 
10020aa77f6cSMauro Carvalho Chehab static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
10030aa77f6cSMauro Carvalho Chehab {
10040aa77f6cSMauro Carvalho Chehab 	int rc;
10050aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10060aa77f6cSMauro Carvalho Chehab 	rc = videobuf_querybuf(&fh->vb_vidq, p);
10070aa77f6cSMauro Carvalho Chehab 	return rc;
10080aa77f6cSMauro Carvalho Chehab }
10090aa77f6cSMauro Carvalho Chehab 
10100aa77f6cSMauro Carvalho Chehab static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
10110aa77f6cSMauro Carvalho Chehab {
10120aa77f6cSMauro Carvalho Chehab 	int rc;
10130aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10140aa77f6cSMauro Carvalho Chehab 	rc = videobuf_qbuf(&fh->vb_vidq, p);
10150aa77f6cSMauro Carvalho Chehab 	return rc;
10160aa77f6cSMauro Carvalho Chehab }
10170aa77f6cSMauro Carvalho Chehab 
10180aa77f6cSMauro Carvalho Chehab static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
10190aa77f6cSMauro Carvalho Chehab {
10200aa77f6cSMauro Carvalho Chehab 	int rc;
10210aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10220aa77f6cSMauro Carvalho Chehab 	rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
10230aa77f6cSMauro Carvalho Chehab 	return rc;
10240aa77f6cSMauro Carvalho Chehab }
10250aa77f6cSMauro Carvalho Chehab 
10260aa77f6cSMauro Carvalho Chehab /* write to the configuration pipe, synchronously */
10270aa77f6cSMauro Carvalho Chehab static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
10280aa77f6cSMauro Carvalho Chehab 			      int size)
10290aa77f6cSMauro Carvalho Chehab {
10300aa77f6cSMauro Carvalho Chehab 	int pipe;
10310aa77f6cSMauro Carvalho Chehab 	int done;
10320aa77f6cSMauro Carvalho Chehab 	long retval = -1;
10330aa77f6cSMauro Carvalho Chehab 	if (udev) {
10340aa77f6cSMauro Carvalho Chehab 		pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
10350aa77f6cSMauro Carvalho Chehab 		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
10360aa77f6cSMauro Carvalho Chehab 	}
10370aa77f6cSMauro Carvalho Chehab 	return retval;
10380aa77f6cSMauro Carvalho Chehab }
10390aa77f6cSMauro Carvalho Chehab 
10400aa77f6cSMauro Carvalho Chehab static u32 get_transfer_size(struct s2255_mode *mode)
10410aa77f6cSMauro Carvalho Chehab {
10420aa77f6cSMauro Carvalho Chehab 	int linesPerFrame = LINE_SZ_DEF;
10430aa77f6cSMauro Carvalho Chehab 	int pixelsPerLine = NUM_LINES_DEF;
10440aa77f6cSMauro Carvalho Chehab 	u32 outImageSize;
10450aa77f6cSMauro Carvalho Chehab 	u32 usbInSize;
10460aa77f6cSMauro Carvalho Chehab 	unsigned int mask_mult;
10470aa77f6cSMauro Carvalho Chehab 
10480aa77f6cSMauro Carvalho Chehab 	if (mode == NULL)
10490aa77f6cSMauro Carvalho Chehab 		return 0;
10500aa77f6cSMauro Carvalho Chehab 
10510aa77f6cSMauro Carvalho Chehab 	if (mode->format == FORMAT_NTSC) {
10520aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
10530aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
10540aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
10550aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
10560aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
10570aa77f6cSMauro Carvalho Chehab 			break;
10580aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
10590aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_NTSC;
10600aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
10610aa77f6cSMauro Carvalho Chehab 			break;
10620aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
10630aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_NTSC;
10640aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
10650aa77f6cSMauro Carvalho Chehab 			break;
10660aa77f6cSMauro Carvalho Chehab 		default:
10670aa77f6cSMauro Carvalho Chehab 			break;
10680aa77f6cSMauro Carvalho Chehab 		}
10690aa77f6cSMauro Carvalho Chehab 	} else if (mode->format == FORMAT_PAL) {
10700aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
10710aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
10720aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
10730aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
10740aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_PAL;
10750aa77f6cSMauro Carvalho Chehab 			break;
10760aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
10770aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_PAL;
10780aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_PAL;
10790aa77f6cSMauro Carvalho Chehab 			break;
10800aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
10810aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_PAL;
10820aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_PAL;
10830aa77f6cSMauro Carvalho Chehab 			break;
10840aa77f6cSMauro Carvalho Chehab 		default:
10850aa77f6cSMauro Carvalho Chehab 			break;
10860aa77f6cSMauro Carvalho Chehab 		}
10870aa77f6cSMauro Carvalho Chehab 	}
10880aa77f6cSMauro Carvalho Chehab 	outImageSize = linesPerFrame * pixelsPerLine;
10890aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) != COLOR_Y8) {
10900aa77f6cSMauro Carvalho Chehab 		/* 2 bytes/pixel if not monochrome */
10910aa77f6cSMauro Carvalho Chehab 		outImageSize *= 2;
10920aa77f6cSMauro Carvalho Chehab 	}
10930aa77f6cSMauro Carvalho Chehab 
10940aa77f6cSMauro Carvalho Chehab 	/* total bytes to send including prefix and 4K padding;
10950aa77f6cSMauro Carvalho Chehab 	   must be a multiple of USB_READ_SIZE */
10960aa77f6cSMauro Carvalho Chehab 	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
10970aa77f6cSMauro Carvalho Chehab 	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
10980aa77f6cSMauro Carvalho Chehab 	/* if size not a multiple of USB_READ_SIZE */
10990aa77f6cSMauro Carvalho Chehab 	if (usbInSize & ~mask_mult)
11000aa77f6cSMauro Carvalho Chehab 		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
11010aa77f6cSMauro Carvalho Chehab 	return usbInSize;
11020aa77f6cSMauro Carvalho Chehab }
11030aa77f6cSMauro Carvalho Chehab 
11040aa77f6cSMauro Carvalho Chehab static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
11050aa77f6cSMauro Carvalho Chehab {
11060aa77f6cSMauro Carvalho Chehab 	struct device *dev = &sdev->udev->dev;
11070aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
11080aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
11090aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
11100aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "bright: 0x%x\n", mode->bright);
11110aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
11120aa77f6cSMauro Carvalho Chehab }
11130aa77f6cSMauro Carvalho Chehab 
11140aa77f6cSMauro Carvalho Chehab /*
11150aa77f6cSMauro Carvalho Chehab  * set mode is the function which controls the DSP.
11160aa77f6cSMauro Carvalho Chehab  * the restart parameter in struct s2255_mode should be set whenever
11170aa77f6cSMauro Carvalho Chehab  * the image size could change via color format, video system or image
11180aa77f6cSMauro Carvalho Chehab  * size.
11190aa77f6cSMauro Carvalho Chehab  * When the restart parameter is set, we sleep for ONE frame to allow the
11200aa77f6cSMauro Carvalho Chehab  * DSP time to get the new frame
11210aa77f6cSMauro Carvalho Chehab  */
11225e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc,
11230aa77f6cSMauro Carvalho Chehab 			  struct s2255_mode *mode)
11240aa77f6cSMauro Carvalho Chehab {
11250aa77f6cSMauro Carvalho Chehab 	int res;
11260aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
11275e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
11280b84caabSHans Verkuil 	int i;
112947d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
11300b84caabSHans Verkuil 
113147d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
11325e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
11335e950fafSDean Anderson 	dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx);
11340aa77f6cSMauro Carvalho Chehab 	/* if JPEG, set the quality */
11350aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) == COLOR_JPG) {
11360aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_COLOR;
11370aa77f6cSMauro Carvalho Chehab 		mode->color |= COLOR_JPG;
11380aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_JPG_QUALITY;
11395e950fafSDean Anderson 		mode->color |= (vc->jpegqual << 8);
11400aa77f6cSMauro Carvalho Chehab 	}
11410aa77f6cSMauro Carvalho Chehab 	/* save the mode */
11425e950fafSDean Anderson 	vc->mode = *mode;
11435e950fafSDean Anderson 	vc->req_image_size = get_transfer_size(mode);
11445e950fafSDean Anderson 	dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size);
11450aa77f6cSMauro Carvalho Chehab 	/* set the mode */
11460aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
11470aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
11480aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_SET_MODE;
11490b84caabSHans Verkuil 	for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++)
11505e950fafSDean Anderson 		buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]);
11515e950fafSDean Anderson 	vc->setmode_ready = 0;
11520aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
11530aa77f6cSMauro Carvalho Chehab 	if (debug)
11540aa77f6cSMauro Carvalho Chehab 		s2255_print_cfg(dev, mode);
11550aa77f6cSMauro Carvalho Chehab 	/* wait at least 3 frames before continuing */
11560aa77f6cSMauro Carvalho Chehab 	if (mode->restart) {
11575e950fafSDean Anderson 		wait_event_timeout(vc->wait_setmode,
11585e950fafSDean Anderson 				   (vc->setmode_ready != 0),
11590aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
11605e950fafSDean Anderson 		if (vc->setmode_ready != 1) {
1161f5402007Ssensoray-dev 			dprintk(dev, 0, "s2255: no set mode response\n");
11620aa77f6cSMauro Carvalho Chehab 			res = -EFAULT;
11630aa77f6cSMauro Carvalho Chehab 		}
11640aa77f6cSMauro Carvalho Chehab 	}
11650aa77f6cSMauro Carvalho Chehab 	/* clear the restart flag */
11665e950fafSDean Anderson 	vc->mode.restart = 0;
11675e950fafSDean Anderson 	dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res);
116847d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
11690aa77f6cSMauro Carvalho Chehab 	return res;
11700aa77f6cSMauro Carvalho Chehab }
11710aa77f6cSMauro Carvalho Chehab 
11725e950fafSDean Anderson static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus)
11730aa77f6cSMauro Carvalho Chehab {
11740aa77f6cSMauro Carvalho Chehab 	int res;
11750aa77f6cSMauro Carvalho Chehab 	u32 chn_rev;
11765e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
117747d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
117847d8c881SDean Anderson 
117947d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
11805e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
11815e950fafSDean Anderson 	dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx);
11820aa77f6cSMauro Carvalho Chehab 	/* form the get vid status command */
11830aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
11840aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
11850aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_STATUS;
11860aa77f6cSMauro Carvalho Chehab 	*pstatus = 0;
11875e950fafSDean Anderson 	vc->vidstatus_ready = 0;
11880aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
11895e950fafSDean Anderson 	wait_event_timeout(vc->wait_vidstatus,
11905e950fafSDean Anderson 			   (vc->vidstatus_ready != 0),
11910aa77f6cSMauro Carvalho Chehab 			   msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
11925e950fafSDean Anderson 	if (vc->vidstatus_ready != 1) {
1193f5402007Ssensoray-dev 		dprintk(dev, 0, "s2255: no vidstatus response\n");
11940aa77f6cSMauro Carvalho Chehab 		res = -EFAULT;
11950aa77f6cSMauro Carvalho Chehab 	}
11965e950fafSDean Anderson 	*pstatus = vc->vidstatus;
1197f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus);
119847d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
11990aa77f6cSMauro Carvalho Chehab 	return res;
12000aa77f6cSMauro Carvalho Chehab }
12010aa77f6cSMauro Carvalho Chehab 
12020aa77f6cSMauro Carvalho Chehab static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
12030aa77f6cSMauro Carvalho Chehab {
12040aa77f6cSMauro Carvalho Chehab 	int res;
12050aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
12065e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
1207*92cde477SDean Anderson 	struct s2255_dev *dev = vc->dev;
12080aa77f6cSMauro Carvalho Chehab 	int j;
1209*92cde477SDean Anderson 
1210f5402007Ssensoray-dev 	dprintk(dev, 4, "%s\n", __func__);
1211*92cde477SDean Anderson 	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
12120aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid fh type1\n");
12130aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12140aa77f6cSMauro Carvalho Chehab 	}
12150aa77f6cSMauro Carvalho Chehab 
12160aa77f6cSMauro Carvalho Chehab 	if (!res_get(fh)) {
12170aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&dev->udev->dev, "stream busy\n");
12180aa77f6cSMauro Carvalho Chehab 		return -EBUSY;
12190aa77f6cSMauro Carvalho Chehab 	}
12205e950fafSDean Anderson 	vc->last_frame = -1;
12215e950fafSDean Anderson 	vc->bad_payload = 0;
12225e950fafSDean Anderson 	vc->cur_frame = 0;
12235e950fafSDean Anderson 	vc->frame_count = 0;
12240aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
12255e950fafSDean Anderson 		vc->buffer.frame[j].ulState = S2255_READ_IDLE;
12265e950fafSDean Anderson 		vc->buffer.frame[j].cur_size = 0;
12270aa77f6cSMauro Carvalho Chehab 	}
12280aa77f6cSMauro Carvalho Chehab 	res = videobuf_streamon(&fh->vb_vidq);
12296a5b63b3SDean Anderson 	if (res != 0) {
12300aa77f6cSMauro Carvalho Chehab 		res_free(fh);
12316a5b63b3SDean Anderson 		return res;
12326a5b63b3SDean Anderson 	}
12336a5b63b3SDean Anderson 	res = s2255_start_acquire(vc);
12346a5b63b3SDean Anderson 	if (res != 0) {
12356a5b63b3SDean Anderson 		res_free(fh);
12366a5b63b3SDean Anderson 		return res;
12376a5b63b3SDean Anderson 	}
12386a5b63b3SDean Anderson 	vc->b_acquire = 1;
12390aa77f6cSMauro Carvalho Chehab 	return res;
12400aa77f6cSMauro Carvalho Chehab }
12410aa77f6cSMauro Carvalho Chehab 
12420aa77f6cSMauro Carvalho Chehab static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
12430aa77f6cSMauro Carvalho Chehab {
12440aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
1245*92cde477SDean Anderson 	struct s2255_vc *vc = fh->vc;
1246*92cde477SDean Anderson 	dprintk(vc->dev, 4, "%s\n, channel: %d", __func__, vc->idx);
1247*92cde477SDean Anderson 
1248*92cde477SDean Anderson 	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
12490aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
1250*92cde477SDean Anderson 	s2255_stop_acquire(vc);
12510aa77f6cSMauro Carvalho Chehab 	videobuf_streamoff(&fh->vb_vidq);
12520aa77f6cSMauro Carvalho Chehab 	res_free(fh);
12530aa77f6cSMauro Carvalho Chehab 	return 0;
12540aa77f6cSMauro Carvalho Chehab }
12550aa77f6cSMauro Carvalho Chehab 
1256314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
12570aa77f6cSMauro Carvalho Chehab {
12580aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
12590aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
12600aa77f6cSMauro Carvalho Chehab 	struct videobuf_queue *q = &fh->vb_vidq;
12615e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
12620aa77f6cSMauro Carvalho Chehab 	int ret = 0;
1263469af77aSHans Verkuil 
12640aa77f6cSMauro Carvalho Chehab 	mutex_lock(&q->vb_lock);
12650aa77f6cSMauro Carvalho Chehab 	if (res_locked(fh)) {
1266*92cde477SDean Anderson 		dprintk(vc->dev, 1, "can't change standard after started\n");
12670aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
12680aa77f6cSMauro Carvalho Chehab 		goto out_s_std;
12690aa77f6cSMauro Carvalho Chehab 	}
1270*92cde477SDean Anderson 	mode = vc->mode;
1271314527acSHans Verkuil 	if (i & V4L2_STD_525_60) {
1272*92cde477SDean Anderson 		dprintk(vc->dev, 4, "%s 60 Hz\n", __func__);
12730aa77f6cSMauro Carvalho Chehab 		/* if changing format, reset frame decimation/intervals */
12740aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_NTSC) {
12750aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
12760aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_NTSC;
12770aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
12785e950fafSDean Anderson 			vc->width = LINE_SZ_4CIFS_NTSC;
12795e950fafSDean Anderson 			vc->height = NUM_LINES_4CIFS_NTSC * 2;
12800aa77f6cSMauro Carvalho Chehab 		}
1281314527acSHans Verkuil 	} else if (i & V4L2_STD_625_50) {
1282*92cde477SDean Anderson 		dprintk(vc->dev, 4, "%s 50 Hz\n", __func__);
12830aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_PAL) {
12840aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
12850aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_PAL;
12860aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
12875e950fafSDean Anderson 			vc->width = LINE_SZ_4CIFS_PAL;
12885e950fafSDean Anderson 			vc->height = NUM_LINES_4CIFS_PAL * 2;
12890aa77f6cSMauro Carvalho Chehab 		}
12900aa77f6cSMauro Carvalho Chehab 	} else {
12910aa77f6cSMauro Carvalho Chehab 		ret = -EINVAL;
1292469af77aSHans Verkuil 		goto out_s_std;
12930aa77f6cSMauro Carvalho Chehab 	}
1294*92cde477SDean Anderson 	vc->std = i;
12950aa77f6cSMauro Carvalho Chehab 	if (mode.restart)
1296*92cde477SDean Anderson 		s2255_set_mode(vc, &mode);
12970aa77f6cSMauro Carvalho Chehab out_s_std:
12980aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&q->vb_lock);
12990aa77f6cSMauro Carvalho Chehab 	return ret;
13000aa77f6cSMauro Carvalho Chehab }
13010aa77f6cSMauro Carvalho Chehab 
1302469af77aSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
1303469af77aSHans Verkuil {
1304469af77aSHans Verkuil 	struct s2255_fh *fh = priv;
1305*92cde477SDean Anderson 	struct s2255_vc *vc = fh->vc;
1306469af77aSHans Verkuil 
1307*92cde477SDean Anderson 	*i = vc->std;
1308469af77aSHans Verkuil 	return 0;
1309469af77aSHans Verkuil }
1310469af77aSHans Verkuil 
13110aa77f6cSMauro Carvalho Chehab /* Sensoray 2255 is a multiple channel capture device.
13120aa77f6cSMauro Carvalho Chehab    It does not have a "crossbar" of inputs.
13130aa77f6cSMauro Carvalho Chehab    We use one V4L device per channel. The user must
13140aa77f6cSMauro Carvalho Chehab    be aware that certain combinations are not allowed.
13150aa77f6cSMauro Carvalho Chehab    For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
13160aa77f6cSMauro Carvalho Chehab    at once in color(you can do full fps on 4 channels with greyscale.
13170aa77f6cSMauro Carvalho Chehab */
13180aa77f6cSMauro Carvalho Chehab static int vidioc_enum_input(struct file *file, void *priv,
13190aa77f6cSMauro Carvalho Chehab 			     struct v4l2_input *inp)
13200aa77f6cSMauro Carvalho Chehab {
13210aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
13225e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
1323*92cde477SDean Anderson 	struct s2255_dev *dev = vc->dev;
13240aa77f6cSMauro Carvalho Chehab 	u32 status = 0;
1325*92cde477SDean Anderson 
13260aa77f6cSMauro Carvalho Chehab 	if (inp->index != 0)
13270aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13280aa77f6cSMauro Carvalho Chehab 	inp->type = V4L2_INPUT_TYPE_CAMERA;
13290aa77f6cSMauro Carvalho Chehab 	inp->std = S2255_NORMS;
13300aa77f6cSMauro Carvalho Chehab 	inp->status = 0;
13310aa77f6cSMauro Carvalho Chehab 	if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
13320aa77f6cSMauro Carvalho Chehab 		int rc;
1333*92cde477SDean Anderson 		rc = s2255_cmd_status(vc, &status);
1334f5402007Ssensoray-dev 		dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n",
1335f5402007Ssensoray-dev 			rc, status);
13360aa77f6cSMauro Carvalho Chehab 		if (rc == 0)
13370aa77f6cSMauro Carvalho Chehab 			inp->status =  (status & 0x01) ? 0
13380aa77f6cSMauro Carvalho Chehab 				: V4L2_IN_ST_NO_SIGNAL;
13390aa77f6cSMauro Carvalho Chehab 	}
13400aa77f6cSMauro Carvalho Chehab 	switch (dev->pid) {
13410aa77f6cSMauro Carvalho Chehab 	case 0x2255:
13420aa77f6cSMauro Carvalho Chehab 	default:
13430aa77f6cSMauro Carvalho Chehab 		strlcpy(inp->name, "Composite", sizeof(inp->name));
13440aa77f6cSMauro Carvalho Chehab 		break;
13450aa77f6cSMauro Carvalho Chehab 	case 0x2257:
13465e950fafSDean Anderson 		strlcpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video",
13470aa77f6cSMauro Carvalho Chehab 			sizeof(inp->name));
13480aa77f6cSMauro Carvalho Chehab 		break;
13490aa77f6cSMauro Carvalho Chehab 	}
13500aa77f6cSMauro Carvalho Chehab 	return 0;
13510aa77f6cSMauro Carvalho Chehab }
13520aa77f6cSMauro Carvalho Chehab 
13530aa77f6cSMauro Carvalho Chehab static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
13540aa77f6cSMauro Carvalho Chehab {
13550aa77f6cSMauro Carvalho Chehab 	*i = 0;
13560aa77f6cSMauro Carvalho Chehab 	return 0;
13570aa77f6cSMauro Carvalho Chehab }
13580aa77f6cSMauro Carvalho Chehab static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
13590aa77f6cSMauro Carvalho Chehab {
13600aa77f6cSMauro Carvalho Chehab 	if (i > 0)
13610aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13620aa77f6cSMauro Carvalho Chehab 	return 0;
13630aa77f6cSMauro Carvalho Chehab }
13640aa77f6cSMauro Carvalho Chehab 
1365192f1e78SHans Verkuil static int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
13660aa77f6cSMauro Carvalho Chehab {
13675e950fafSDean Anderson 	struct s2255_vc *vc =
13685e950fafSDean Anderson 		container_of(ctrl->handler, struct s2255_vc, hdl);
13690aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
13705e950fafSDean Anderson 	mode = vc->mode;
13710aa77f6cSMauro Carvalho Chehab 	/* update the mode to the corresponding value */
13720aa77f6cSMauro Carvalho Chehab 	switch (ctrl->id) {
13730aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1374192f1e78SHans Verkuil 		mode.bright = ctrl->val;
13750aa77f6cSMauro Carvalho Chehab 		break;
13760aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1377192f1e78SHans Verkuil 		mode.contrast = ctrl->val;
13780aa77f6cSMauro Carvalho Chehab 		break;
13790aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_HUE:
1380192f1e78SHans Verkuil 		mode.hue = ctrl->val;
13810aa77f6cSMauro Carvalho Chehab 		break;
13820aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1383192f1e78SHans Verkuil 		mode.saturation = ctrl->val;
13840aa77f6cSMauro Carvalho Chehab 		break;
1385192f1e78SHans Verkuil 	case V4L2_CID_S2255_COLORFILTER:
13860aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_INPUT_TYPE;
1387192f1e78SHans Verkuil 		mode.color |= !ctrl->val << 16;
13880aa77f6cSMauro Carvalho Chehab 		break;
13897041dec7SHans Verkuil 	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
13905e950fafSDean Anderson 		vc->jpegqual = ctrl->val;
13917041dec7SHans Verkuil 		return 0;
13920aa77f6cSMauro Carvalho Chehab 	default:
13930aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13940aa77f6cSMauro Carvalho Chehab 	}
13950aa77f6cSMauro Carvalho Chehab 	mode.restart = 0;
13960aa77f6cSMauro Carvalho Chehab 	/* set mode here.  Note: stream does not need restarted.
13970aa77f6cSMauro Carvalho Chehab 	   some V4L programs restart stream unnecessarily
13980aa77f6cSMauro Carvalho Chehab 	   after a s_crtl.
13990aa77f6cSMauro Carvalho Chehab 	*/
14005e950fafSDean Anderson 	s2255_set_mode(vc, &mode);
14010aa77f6cSMauro Carvalho Chehab 	return 0;
14020aa77f6cSMauro Carvalho Chehab }
14030aa77f6cSMauro Carvalho Chehab 
14040aa77f6cSMauro Carvalho Chehab static int vidioc_g_jpegcomp(struct file *file, void *priv,
14050aa77f6cSMauro Carvalho Chehab 			 struct v4l2_jpegcompression *jc)
14060aa77f6cSMauro Carvalho Chehab {
14070aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
14085e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
14097041dec7SHans Verkuil 
14107041dec7SHans Verkuil 	memset(jc, 0, sizeof(*jc));
14115e950fafSDean Anderson 	jc->quality = vc->jpegqual;
1412*92cde477SDean Anderson 	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
14130aa77f6cSMauro Carvalho Chehab 	return 0;
14140aa77f6cSMauro Carvalho Chehab }
14150aa77f6cSMauro Carvalho Chehab 
14160aa77f6cSMauro Carvalho Chehab static int vidioc_s_jpegcomp(struct file *file, void *priv,
1417d88aab53SHans Verkuil 			 const struct v4l2_jpegcompression *jc)
14180aa77f6cSMauro Carvalho Chehab {
14190aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
14205e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
14210aa77f6cSMauro Carvalho Chehab 	if (jc->quality < 0 || jc->quality > 100)
14220aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
14235e950fafSDean Anderson 	v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality);
1424*92cde477SDean Anderson 	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
14250aa77f6cSMauro Carvalho Chehab 	return 0;
14260aa77f6cSMauro Carvalho Chehab }
14270aa77f6cSMauro Carvalho Chehab 
14280aa77f6cSMauro Carvalho Chehab static int vidioc_g_parm(struct file *file, void *priv,
14290aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
14300aa77f6cSMauro Carvalho Chehab {
14310aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
14320aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
14335e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
14340aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
14350aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
14360aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
14375e950fafSDean Anderson 	sp->parm.capture.capturemode = vc->cap_parm.capturemode;
14385e950fafSDean Anderson 	def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000;
14395e950fafSDean Anderson 	def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000;
14400aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
14415e950fafSDean Anderson 	switch (vc->mode.fdec) {
14420aa77f6cSMauro Carvalho Chehab 	default:
14430aa77f6cSMauro Carvalho Chehab 	case FDEC_1:
14440aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
14450aa77f6cSMauro Carvalho Chehab 		break;
14460aa77f6cSMauro Carvalho Chehab 	case FDEC_2:
14470aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
14480aa77f6cSMauro Carvalho Chehab 		break;
14490aa77f6cSMauro Carvalho Chehab 	case FDEC_3:
14500aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
14510aa77f6cSMauro Carvalho Chehab 		break;
14520aa77f6cSMauro Carvalho Chehab 	case FDEC_5:
14530aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
14540aa77f6cSMauro Carvalho Chehab 		break;
14550aa77f6cSMauro Carvalho Chehab 	}
1456*92cde477SDean Anderson 	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n",
1457f5402007Ssensoray-dev 		__func__,
14580aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
14590aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
14600aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator);
14610aa77f6cSMauro Carvalho Chehab 	return 0;
14620aa77f6cSMauro Carvalho Chehab }
14630aa77f6cSMauro Carvalho Chehab 
14640aa77f6cSMauro Carvalho Chehab static int vidioc_s_parm(struct file *file, void *priv,
14650aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
14660aa77f6cSMauro Carvalho Chehab {
14670aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
14685e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
14690aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
14700aa77f6cSMauro Carvalho Chehab 	int fdec = FDEC_1;
14710aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
14720aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
14730aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
14745e950fafSDean Anderson 	mode = vc->mode;
14750aa77f6cSMauro Carvalho Chehab 	/* high quality capture mode requires a stream restart */
14765e950fafSDean Anderson 	if (vc->cap_parm.capturemode
14770aa77f6cSMauro Carvalho Chehab 	    != sp->parm.capture.capturemode && res_locked(fh))
14780aa77f6cSMauro Carvalho Chehab 		return -EBUSY;
14790aa77f6cSMauro Carvalho Chehab 	def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
14800aa77f6cSMauro Carvalho Chehab 	def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
14810aa77f6cSMauro Carvalho Chehab 	if (def_dem != sp->parm.capture.timeperframe.denominator)
14820aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
14830aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= def_num)
14840aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
14850aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
14860aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
14870aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_2;
14880aa77f6cSMauro Carvalho Chehab 	} else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
14890aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
14900aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_3;
14910aa77f6cSMauro Carvalho Chehab 	} else {
14920aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
14930aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_5;
14940aa77f6cSMauro Carvalho Chehab 	}
14950aa77f6cSMauro Carvalho Chehab 	mode.fdec = fdec;
14960aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
14975e950fafSDean Anderson 	s2255_set_mode(vc, &mode);
1498*92cde477SDean Anderson 	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
14990aa77f6cSMauro Carvalho Chehab 		__func__,
15000aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
15010aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
15020aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator, fdec);
15030aa77f6cSMauro Carvalho Chehab 	return 0;
15040aa77f6cSMauro Carvalho Chehab }
15050aa77f6cSMauro Carvalho Chehab 
150605e5d44bSHans Verkuil #define NUM_SIZE_ENUMS 3
150705e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete ntsc_sizes[] = {
150805e5d44bSHans Verkuil 	{ 640, 480 },
150905e5d44bSHans Verkuil 	{ 640, 240 },
151005e5d44bSHans Verkuil 	{ 320, 240 },
151105e5d44bSHans Verkuil };
151205e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete pal_sizes[] = {
151305e5d44bSHans Verkuil 	{ 704, 576 },
151405e5d44bSHans Verkuil 	{ 704, 288 },
151505e5d44bSHans Verkuil 	{ 352, 288 },
151605e5d44bSHans Verkuil };
151705e5d44bSHans Verkuil 
151805e5d44bSHans Verkuil static int vidioc_enum_framesizes(struct file *file, void *priv,
151905e5d44bSHans Verkuil 			    struct v4l2_frmsizeenum *fe)
152005e5d44bSHans Verkuil {
152105e5d44bSHans Verkuil 	struct s2255_fh *fh = priv;
15225e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
15235e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
152405e5d44bSHans Verkuil 	const struct s2255_fmt *fmt;
152505e5d44bSHans Verkuil 
152605e5d44bSHans Verkuil 	if (fe->index >= NUM_SIZE_ENUMS)
152705e5d44bSHans Verkuil 		return -EINVAL;
152805e5d44bSHans Verkuil 
152905e5d44bSHans Verkuil 	fmt = format_by_fourcc(fe->pixel_format);
153005e5d44bSHans Verkuil 	if (fmt == NULL)
153105e5d44bSHans Verkuil 		return -EINVAL;
153205e5d44bSHans Verkuil 	fe->type = V4L2_FRMSIZE_TYPE_DISCRETE;
153305e5d44bSHans Verkuil 	fe->discrete = is_ntsc ?  ntsc_sizes[fe->index] : pal_sizes[fe->index];
153405e5d44bSHans Verkuil 	return 0;
153505e5d44bSHans Verkuil }
153605e5d44bSHans Verkuil 
15370aa77f6cSMauro Carvalho Chehab static int vidioc_enum_frameintervals(struct file *file, void *priv,
15380aa77f6cSMauro Carvalho Chehab 			    struct v4l2_frmivalenum *fe)
15390aa77f6cSMauro Carvalho Chehab {
154005e5d44bSHans Verkuil 	struct s2255_fh *fh = priv;
15415e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
154205e5d44bSHans Verkuil 	const struct s2255_fmt *fmt;
154305e5d44bSHans Verkuil 	const struct v4l2_frmsize_discrete *sizes;
15445e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
15450aa77f6cSMauro Carvalho Chehab #define NUM_FRAME_ENUMS 4
15460aa77f6cSMauro Carvalho Chehab 	int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
154705e5d44bSHans Verkuil 	int i;
154805e5d44bSHans Verkuil 
1549199ab8feSMauro Carvalho Chehab 	if (fe->index >= NUM_FRAME_ENUMS)
15500aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
155105e5d44bSHans Verkuil 
155205e5d44bSHans Verkuil 	fmt = format_by_fourcc(fe->pixel_format);
155305e5d44bSHans Verkuil 	if (fmt == NULL)
15540aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
155505e5d44bSHans Verkuil 
155605e5d44bSHans Verkuil 	sizes = is_ntsc ? ntsc_sizes : pal_sizes;
155705e5d44bSHans Verkuil 	for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++)
155805e5d44bSHans Verkuil 		if (fe->width == sizes->width &&
155905e5d44bSHans Verkuil 		    fe->height == sizes->height)
15600aa77f6cSMauro Carvalho Chehab 			break;
156105e5d44bSHans Verkuil 	if (i == NUM_SIZE_ENUMS)
15620aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
156305e5d44bSHans Verkuil 
15640aa77f6cSMauro Carvalho Chehab 	fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
15650aa77f6cSMauro Carvalho Chehab 	fe->discrete.denominator = is_ntsc ? 30000 : 25000;
15660aa77f6cSMauro Carvalho Chehab 	fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
1567*92cde477SDean Anderson 	dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__,
1568f5402007Ssensoray-dev 		fe->discrete.numerator,
15690aa77f6cSMauro Carvalho Chehab 		fe->discrete.denominator);
15700aa77f6cSMauro Carvalho Chehab 	return 0;
15710aa77f6cSMauro Carvalho Chehab }
15720aa77f6cSMauro Carvalho Chehab 
15730aa77f6cSMauro Carvalho Chehab static int __s2255_open(struct file *file)
15740aa77f6cSMauro Carvalho Chehab {
15750aa77f6cSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
15765e950fafSDean Anderson 	struct s2255_vc *vc = video_drvdata(file);
15770aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
15780aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh;
15790aa77f6cSMauro Carvalho Chehab 	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
15800aa77f6cSMauro Carvalho Chehab 	int state;
1581f5402007Ssensoray-dev 	dprintk(dev, 1, "s2255: open called (dev=%s)\n",
15820aa77f6cSMauro Carvalho Chehab 		video_device_node_name(vdev));
15830aa77f6cSMauro Carvalho Chehab 	state = atomic_read(&dev->fw_data->fw_state);
15840aa77f6cSMauro Carvalho Chehab 	switch (state) {
15850aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
15860aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
15870aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
15880aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&dev->udev->dev,
15890aa77f6cSMauro Carvalho Chehab 			"firmware load failed. retrying.\n");
15900aa77f6cSMauro Carvalho Chehab 		s2255_fwload_start(dev, 1);
15910aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
15920aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
15930aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
15940aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
15950aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
15960aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
15970aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
15980aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
15990aa77f6cSMauro Carvalho Chehab 		break;
16000aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
16010aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
16020aa77f6cSMauro Carvalho Chehab 		/* give S2255_LOAD_TIMEOUT time for firmware to load in case
16030aa77f6cSMauro Carvalho Chehab 		   driver loaded and then device immediately opened */
1604f5402007Ssensoray-dev 		pr_info("%s waiting for firmware load\n", __func__);
16050aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
16060aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
16070aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
16080aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
16090aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
16100aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
16110aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
16120aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
16130aa77f6cSMauro Carvalho Chehab 		break;
16140aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
16150aa77f6cSMauro Carvalho Chehab 	default:
16160aa77f6cSMauro Carvalho Chehab 		break;
16170aa77f6cSMauro Carvalho Chehab 	}
16180aa77f6cSMauro Carvalho Chehab 	/* state may have changed in above switch statement */
16190aa77f6cSMauro Carvalho Chehab 	switch (state) {
16200aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
16210aa77f6cSMauro Carvalho Chehab 		break;
16220aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
1623f5402007Ssensoray-dev 		pr_info("2255 firmware load failed.\n");
16240aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
16250aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
1626f5402007Ssensoray-dev 		pr_info("%s: disconnecting\n", __func__);
16270aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
16280aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
16290aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
1630f5402007Ssensoray-dev 		pr_info("%s: firmware not loaded, please retry\n",
16310aa77f6cSMauro Carvalho Chehab 			__func__);
16320aa77f6cSMauro Carvalho Chehab 		/*
16330aa77f6cSMauro Carvalho Chehab 		 * Timeout on firmware load means device unusable.
16340aa77f6cSMauro Carvalho Chehab 		 * Set firmware failure state.
16350aa77f6cSMauro Carvalho Chehab 		 * On next s2255_open the firmware will be reloaded.
16360aa77f6cSMauro Carvalho Chehab 		 */
16370aa77f6cSMauro Carvalho Chehab 		atomic_set(&dev->fw_data->fw_state,
16380aa77f6cSMauro Carvalho Chehab 			   S2255_FW_FAILED);
16390aa77f6cSMauro Carvalho Chehab 		return -EAGAIN;
16400aa77f6cSMauro Carvalho Chehab 	default:
1641f5402007Ssensoray-dev 		pr_info("%s: unknown state\n", __func__);
16420aa77f6cSMauro Carvalho Chehab 		return -EFAULT;
16430aa77f6cSMauro Carvalho Chehab 	}
16440aa77f6cSMauro Carvalho Chehab 	/* allocate + initialize per filehandle data */
16450aa77f6cSMauro Carvalho Chehab 	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
16460aa77f6cSMauro Carvalho Chehab 	if (NULL == fh)
16470aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
164844d06d82SHans Verkuil 	v4l2_fh_init(&fh->fh, vdev);
164944d06d82SHans Verkuil 	v4l2_fh_add(&fh->fh);
165044d06d82SHans Verkuil 	file->private_data = &fh->fh;
16515e950fafSDean Anderson 	fh->vc = vc;
16525e950fafSDean Anderson 	if (!vc->configured) {
16530aa77f6cSMauro Carvalho Chehab 		/* configure channel to default state */
16545e950fafSDean Anderson 		vc->fmt = &formats[0];
16555e950fafSDean Anderson 		s2255_set_mode(vc, &vc->mode);
16565e950fafSDean Anderson 		vc->configured = 1;
16570aa77f6cSMauro Carvalho Chehab 	}
1658f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: dev=%s type=%s\n", __func__,
16590aa77f6cSMauro Carvalho Chehab 		video_device_node_name(vdev), v4l2_type_names[type]);
1660d86c6a8cSDean Anderson 	dprintk(dev, 2, "%s: fh=0x%08lx, dev=0x%08lx\n", __func__,
1661d86c6a8cSDean Anderson 		(unsigned long)fh, (unsigned long)dev);
1662f5402007Ssensoray-dev 	dprintk(dev, 4, "%s: list_empty active=%d\n", __func__,
16635e950fafSDean Anderson 		list_empty(&vc->buf_list));
16640aa77f6cSMauro Carvalho Chehab 	videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
16650aa77f6cSMauro Carvalho Chehab 				    NULL, &dev->slock,
1666*92cde477SDean Anderson 				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
16670aa77f6cSMauro Carvalho Chehab 				    V4L2_FIELD_INTERLACED,
16680aa77f6cSMauro Carvalho Chehab 				    sizeof(struct s2255_buffer),
16690aa77f6cSMauro Carvalho Chehab 				    fh, vdev->lock);
16700aa77f6cSMauro Carvalho Chehab 	return 0;
16710aa77f6cSMauro Carvalho Chehab }
16720aa77f6cSMauro Carvalho Chehab 
16730aa77f6cSMauro Carvalho Chehab static int s2255_open(struct file *file)
16740aa77f6cSMauro Carvalho Chehab {
16750aa77f6cSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
16760aa77f6cSMauro Carvalho Chehab 	int ret;
16770aa77f6cSMauro Carvalho Chehab 
16780aa77f6cSMauro Carvalho Chehab 	if (mutex_lock_interruptible(vdev->lock))
16790aa77f6cSMauro Carvalho Chehab 		return -ERESTARTSYS;
16800aa77f6cSMauro Carvalho Chehab 	ret = __s2255_open(file);
16810aa77f6cSMauro Carvalho Chehab 	mutex_unlock(vdev->lock);
16820aa77f6cSMauro Carvalho Chehab 	return ret;
16830aa77f6cSMauro Carvalho Chehab }
16840aa77f6cSMauro Carvalho Chehab 
16850aa77f6cSMauro Carvalho Chehab static unsigned int s2255_poll(struct file *file,
16860aa77f6cSMauro Carvalho Chehab 			       struct poll_table_struct *wait)
16870aa77f6cSMauro Carvalho Chehab {
16880aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
1689*92cde477SDean Anderson 	struct s2255_dev *dev = fh->vc->dev;
169044d06d82SHans Verkuil 	int rc = v4l2_ctrl_poll(file, wait);
169144d06d82SHans Verkuil 
1692f5402007Ssensoray-dev 	dprintk(dev, 100, "%s\n", __func__);
16930aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
169444d06d82SHans Verkuil 	rc |= videobuf_poll_stream(file, &fh->vb_vidq, wait);
16950aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
16960aa77f6cSMauro Carvalho Chehab 	return rc;
16970aa77f6cSMauro Carvalho Chehab }
16980aa77f6cSMauro Carvalho Chehab 
16990aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev)
17000aa77f6cSMauro Carvalho Chehab {
1701f5402007Ssensoray-dev 	dprintk(dev, 1, "%s", __func__);
17020aa77f6cSMauro Carvalho Chehab 	/* board shutdown stops the read pipe if it is running */
17030aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
17040aa77f6cSMauro Carvalho Chehab 	/* make sure firmware still not trying to load */
17050aa77f6cSMauro Carvalho Chehab 	del_timer(&dev->timer);  /* only started in .probe and .open */
17060aa77f6cSMauro Carvalho Chehab 	if (dev->fw_data->fw_urb) {
17070aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(dev->fw_data->fw_urb);
17080aa77f6cSMauro Carvalho Chehab 		usb_free_urb(dev->fw_data->fw_urb);
17090aa77f6cSMauro Carvalho Chehab 		dev->fw_data->fw_urb = NULL;
17100aa77f6cSMauro Carvalho Chehab 	}
17110aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
17120aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
17130aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
17140aa77f6cSMauro Carvalho Chehab 	/* reset the DSP so firmware can be reloaded next time */
17150aa77f6cSMauro Carvalho Chehab 	s2255_reset_dsppower(dev);
17160aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
17170aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
17180aa77f6cSMauro Carvalho Chehab 	v4l2_device_unregister(&dev->v4l2_dev);
171947d8c881SDean Anderson 	kfree(dev->cmdbuf);
17200aa77f6cSMauro Carvalho Chehab 	kfree(dev);
17210aa77f6cSMauro Carvalho Chehab }
17220aa77f6cSMauro Carvalho Chehab 
17230aa77f6cSMauro Carvalho Chehab static int s2255_release(struct file *file)
17240aa77f6cSMauro Carvalho Chehab {
17250aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
17260aa77f6cSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
17275e950fafSDean Anderson 	struct s2255_vc *vc = fh->vc;
1728*92cde477SDean Anderson 	struct s2255_dev *dev = vc->dev;
1729*92cde477SDean Anderson 
17300aa77f6cSMauro Carvalho Chehab 	if (!dev)
17310aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
17320aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
17330aa77f6cSMauro Carvalho Chehab 	/* turn off stream */
17340aa77f6cSMauro Carvalho Chehab 	if (res_check(fh)) {
17355e950fafSDean Anderson 		if (vc->b_acquire)
1736*92cde477SDean Anderson 			s2255_stop_acquire(vc);
17370aa77f6cSMauro Carvalho Chehab 		videobuf_streamoff(&fh->vb_vidq);
17380aa77f6cSMauro Carvalho Chehab 		res_free(fh);
17390aa77f6cSMauro Carvalho Chehab 	}
17400aa77f6cSMauro Carvalho Chehab 	videobuf_mmap_free(&fh->vb_vidq);
17410aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
1742f5402007Ssensoray-dev 	dprintk(dev, 1, "%s[%s]\n", __func__, video_device_node_name(vdev));
174344d06d82SHans Verkuil 	v4l2_fh_del(&fh->fh);
174444d06d82SHans Verkuil 	v4l2_fh_exit(&fh->fh);
17450aa77f6cSMauro Carvalho Chehab 	kfree(fh);
17460aa77f6cSMauro Carvalho Chehab 	return 0;
17470aa77f6cSMauro Carvalho Chehab }
17480aa77f6cSMauro Carvalho Chehab 
17490aa77f6cSMauro Carvalho Chehab static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
17500aa77f6cSMauro Carvalho Chehab {
17510aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
1752e839776fSJulia Lawall 	struct s2255_dev *dev;
17530aa77f6cSMauro Carvalho Chehab 	int ret;
1754*92cde477SDean Anderson 
17550aa77f6cSMauro Carvalho Chehab 	if (!fh)
17560aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
1757*92cde477SDean Anderson 	dev = fh->vc->dev;
1758f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma);
17590aa77f6cSMauro Carvalho Chehab 	if (mutex_lock_interruptible(&dev->lock))
17600aa77f6cSMauro Carvalho Chehab 		return -ERESTARTSYS;
17610aa77f6cSMauro Carvalho Chehab 	ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
17620aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
1763f5402007Ssensoray-dev 	dprintk(dev, 4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__,
17640aa77f6cSMauro Carvalho Chehab 		(unsigned long)vma->vm_start,
17650aa77f6cSMauro Carvalho Chehab 		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
17660aa77f6cSMauro Carvalho Chehab 	return ret;
17670aa77f6cSMauro Carvalho Chehab }
17680aa77f6cSMauro Carvalho Chehab 
17690aa77f6cSMauro Carvalho Chehab static const struct v4l2_file_operations s2255_fops_v4l = {
17700aa77f6cSMauro Carvalho Chehab 	.owner = THIS_MODULE,
17710aa77f6cSMauro Carvalho Chehab 	.open = s2255_open,
17720aa77f6cSMauro Carvalho Chehab 	.release = s2255_release,
17730aa77f6cSMauro Carvalho Chehab 	.poll = s2255_poll,
17740aa77f6cSMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,	/* V4L2 ioctl handler */
17750aa77f6cSMauro Carvalho Chehab 	.mmap = s2255_mmap_v4l,
17760aa77f6cSMauro Carvalho Chehab };
17770aa77f6cSMauro Carvalho Chehab 
17780aa77f6cSMauro Carvalho Chehab static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
17790aa77f6cSMauro Carvalho Chehab 	.vidioc_querycap = vidioc_querycap,
17800aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
17810aa77f6cSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
17820aa77f6cSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
17830aa77f6cSMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
17840aa77f6cSMauro Carvalho Chehab 	.vidioc_reqbufs = vidioc_reqbufs,
17850aa77f6cSMauro Carvalho Chehab 	.vidioc_querybuf = vidioc_querybuf,
17860aa77f6cSMauro Carvalho Chehab 	.vidioc_qbuf = vidioc_qbuf,
17870aa77f6cSMauro Carvalho Chehab 	.vidioc_dqbuf = vidioc_dqbuf,
17880aa77f6cSMauro Carvalho Chehab 	.vidioc_s_std = vidioc_s_std,
1789469af77aSHans Verkuil 	.vidioc_g_std = vidioc_g_std,
17900aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_input = vidioc_enum_input,
17910aa77f6cSMauro Carvalho Chehab 	.vidioc_g_input = vidioc_g_input,
17920aa77f6cSMauro Carvalho Chehab 	.vidioc_s_input = vidioc_s_input,
17930aa77f6cSMauro Carvalho Chehab 	.vidioc_streamon = vidioc_streamon,
17940aa77f6cSMauro Carvalho Chehab 	.vidioc_streamoff = vidioc_streamoff,
17950aa77f6cSMauro Carvalho Chehab 	.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
17960aa77f6cSMauro Carvalho Chehab 	.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
17970aa77f6cSMauro Carvalho Chehab 	.vidioc_s_parm = vidioc_s_parm,
17980aa77f6cSMauro Carvalho Chehab 	.vidioc_g_parm = vidioc_g_parm,
179905e5d44bSHans Verkuil 	.vidioc_enum_framesizes = vidioc_enum_framesizes,
18000aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
180144d06d82SHans Verkuil 	.vidioc_log_status  = v4l2_ctrl_log_status,
180244d06d82SHans Verkuil 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
180344d06d82SHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
18040aa77f6cSMauro Carvalho Chehab };
18050aa77f6cSMauro Carvalho Chehab 
18060aa77f6cSMauro Carvalho Chehab static void s2255_video_device_release(struct video_device *vdev)
18070aa77f6cSMauro Carvalho Chehab {
18080aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
18095e950fafSDean Anderson 	struct s2255_vc *vc =
18105e950fafSDean Anderson 		container_of(vdev, struct s2255_vc, vdev);
1811192f1e78SHans Verkuil 
1812f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, chnls: %d\n", __func__,
18130aa77f6cSMauro Carvalho Chehab 		atomic_read(&dev->num_channels));
1814192f1e78SHans Verkuil 
18155e950fafSDean Anderson 	v4l2_ctrl_handler_free(&vc->hdl);
1816f5402007Ssensoray-dev 
18170aa77f6cSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->num_channels))
18180aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
18190aa77f6cSMauro Carvalho Chehab 	return;
18200aa77f6cSMauro Carvalho Chehab }
18210aa77f6cSMauro Carvalho Chehab 
18220aa77f6cSMauro Carvalho Chehab static struct video_device template = {
18230aa77f6cSMauro Carvalho Chehab 	.name = "s2255v",
18240aa77f6cSMauro Carvalho Chehab 	.fops = &s2255_fops_v4l,
18250aa77f6cSMauro Carvalho Chehab 	.ioctl_ops = &s2255_ioctl_ops,
18260aa77f6cSMauro Carvalho Chehab 	.release = s2255_video_device_release,
18270aa77f6cSMauro Carvalho Chehab 	.tvnorms = S2255_NORMS,
18280aa77f6cSMauro Carvalho Chehab };
18290aa77f6cSMauro Carvalho Chehab 
1830192f1e78SHans Verkuil static const struct v4l2_ctrl_ops s2255_ctrl_ops = {
1831192f1e78SHans Verkuil 	.s_ctrl = s2255_s_ctrl,
1832192f1e78SHans Verkuil };
1833192f1e78SHans Verkuil 
1834192f1e78SHans Verkuil static const struct v4l2_ctrl_config color_filter_ctrl = {
1835192f1e78SHans Verkuil 	.ops = &s2255_ctrl_ops,
1836192f1e78SHans Verkuil 	.name = "Color Filter",
1837192f1e78SHans Verkuil 	.id = V4L2_CID_S2255_COLORFILTER,
1838192f1e78SHans Verkuil 	.type = V4L2_CTRL_TYPE_BOOLEAN,
1839192f1e78SHans Verkuil 	.max = 1,
1840192f1e78SHans Verkuil 	.step = 1,
1841192f1e78SHans Verkuil 	.def = 1,
1842192f1e78SHans Verkuil };
1843192f1e78SHans Verkuil 
18440aa77f6cSMauro Carvalho Chehab static int s2255_probe_v4l(struct s2255_dev *dev)
18450aa77f6cSMauro Carvalho Chehab {
18460aa77f6cSMauro Carvalho Chehab 	int ret;
18470aa77f6cSMauro Carvalho Chehab 	int i;
18480aa77f6cSMauro Carvalho Chehab 	int cur_nr = video_nr;
18495e950fafSDean Anderson 	struct s2255_vc *vc;
18500aa77f6cSMauro Carvalho Chehab 	ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
18510aa77f6cSMauro Carvalho Chehab 	if (ret)
18520aa77f6cSMauro Carvalho Chehab 		return ret;
18530aa77f6cSMauro Carvalho Chehab 	/* initialize all video 4 linux */
18540aa77f6cSMauro Carvalho Chehab 	/* register 4 video devices */
18550aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
18565e950fafSDean Anderson 		vc = &dev->vc[i];
18575e950fafSDean Anderson 		INIT_LIST_HEAD(&vc->buf_list);
1858192f1e78SHans Verkuil 
18595e950fafSDean Anderson 		v4l2_ctrl_handler_init(&vc->hdl, 6);
18605e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1861192f1e78SHans Verkuil 				V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT);
18625e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1863192f1e78SHans Verkuil 				V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST);
18645e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1865192f1e78SHans Verkuil 				V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION);
18665e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1867192f1e78SHans Verkuil 				V4L2_CID_HUE, 0, 255, 1, DEF_HUE);
18685e950fafSDean Anderson 		vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl,
18697041dec7SHans Verkuil 				&s2255_ctrl_ops,
18707041dec7SHans Verkuil 				V4L2_CID_JPEG_COMPRESSION_QUALITY,
18717041dec7SHans Verkuil 				0, 100, 1, S2255_DEF_JPEG_QUAL);
1872192f1e78SHans Verkuil 		if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER &&
18735e950fafSDean Anderson 		    (dev->pid != 0x2257 || vc->idx <= 1))
18745e950fafSDean Anderson 			v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl,
1875f5402007Ssensoray-dev 					     NULL);
18765e950fafSDean Anderson 		if (vc->hdl.error) {
18775e950fafSDean Anderson 			ret = vc->hdl.error;
18785e950fafSDean Anderson 			v4l2_ctrl_handler_free(&vc->hdl);
1879192f1e78SHans Verkuil 			dev_err(&dev->udev->dev, "couldn't register control\n");
1880192f1e78SHans Verkuil 			break;
1881192f1e78SHans Verkuil 		}
18820aa77f6cSMauro Carvalho Chehab 		/* register 4 video devices */
18835e950fafSDean Anderson 		vc->vdev = template;
18845e950fafSDean Anderson 		vc->vdev.ctrl_handler = &vc->hdl;
18855e950fafSDean Anderson 		vc->vdev.lock = &dev->lock;
18865e950fafSDean Anderson 		vc->vdev.v4l2_dev = &dev->v4l2_dev;
18875e950fafSDean Anderson 		set_bit(V4L2_FL_USE_FH_PRIO, &vc->vdev.flags);
18885e950fafSDean Anderson 		video_set_drvdata(&vc->vdev, vc);
18890aa77f6cSMauro Carvalho Chehab 		if (video_nr == -1)
18905e950fafSDean Anderson 			ret = video_register_device(&vc->vdev,
18910aa77f6cSMauro Carvalho Chehab 						    VFL_TYPE_GRABBER,
18920aa77f6cSMauro Carvalho Chehab 						    video_nr);
18930aa77f6cSMauro Carvalho Chehab 		else
18945e950fafSDean Anderson 			ret = video_register_device(&vc->vdev,
18950aa77f6cSMauro Carvalho Chehab 						    VFL_TYPE_GRABBER,
18960aa77f6cSMauro Carvalho Chehab 						    cur_nr + i);
18970aa77f6cSMauro Carvalho Chehab 
18980aa77f6cSMauro Carvalho Chehab 		if (ret) {
18990aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev,
19000aa77f6cSMauro Carvalho Chehab 				"failed to register video device!\n");
19010aa77f6cSMauro Carvalho Chehab 			break;
19020aa77f6cSMauro Carvalho Chehab 		}
19030aa77f6cSMauro Carvalho Chehab 		atomic_inc(&dev->num_channels);
19040aa77f6cSMauro Carvalho Chehab 		v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
19055e950fafSDean Anderson 			  video_device_node_name(&vc->vdev));
19060aa77f6cSMauro Carvalho Chehab 
19070aa77f6cSMauro Carvalho Chehab 	}
1908f5402007Ssensoray-dev 	pr_info("Sensoray 2255 V4L driver Revision: %s\n",
19090aa77f6cSMauro Carvalho Chehab 		S2255_VERSION);
19100aa77f6cSMauro Carvalho Chehab 	/* if no channels registered, return error and probe will fail*/
19110aa77f6cSMauro Carvalho Chehab 	if (atomic_read(&dev->num_channels) == 0) {
19120aa77f6cSMauro Carvalho Chehab 		v4l2_device_unregister(&dev->v4l2_dev);
19130aa77f6cSMauro Carvalho Chehab 		return ret;
19140aa77f6cSMauro Carvalho Chehab 	}
19150aa77f6cSMauro Carvalho Chehab 	if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
1916f5402007Ssensoray-dev 		pr_warn("s2255: Not all channels available.\n");
19170aa77f6cSMauro Carvalho Chehab 	return 0;
19180aa77f6cSMauro Carvalho Chehab }
19190aa77f6cSMauro Carvalho Chehab 
19200aa77f6cSMauro Carvalho Chehab /* this function moves the usb stream read pipe data
19210aa77f6cSMauro Carvalho Chehab  * into the system buffers.
19220aa77f6cSMauro Carvalho Chehab  * returns 0 on success, EAGAIN if more data to process( call this
19230aa77f6cSMauro Carvalho Chehab  * function again).
19240aa77f6cSMauro Carvalho Chehab  *
19250aa77f6cSMauro Carvalho Chehab  * Received frame structure:
19260aa77f6cSMauro Carvalho Chehab  * bytes 0-3:  marker : 0x2255DA4AL (S2255_MARKER_FRAME)
19270aa77f6cSMauro Carvalho Chehab  * bytes 4-7:  channel: 0-3
19280aa77f6cSMauro Carvalho Chehab  * bytes 8-11: payload size:  size of the frame
19290aa77f6cSMauro Carvalho Chehab  * bytes 12-payloadsize+12:  frame data
19300aa77f6cSMauro Carvalho Chehab  */
19310aa77f6cSMauro Carvalho Chehab static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
19320aa77f6cSMauro Carvalho Chehab {
19330aa77f6cSMauro Carvalho Chehab 	char *pdest;
19340aa77f6cSMauro Carvalho Chehab 	u32 offset = 0;
19350aa77f6cSMauro Carvalho Chehab 	int bframe = 0;
19360aa77f6cSMauro Carvalho Chehab 	char *psrc;
19370aa77f6cSMauro Carvalho Chehab 	unsigned long copy_size;
19380aa77f6cSMauro Carvalho Chehab 	unsigned long size;
19390aa77f6cSMauro Carvalho Chehab 	s32 idx = -1;
19400aa77f6cSMauro Carvalho Chehab 	struct s2255_framei *frm;
19410aa77f6cSMauro Carvalho Chehab 	unsigned char *pdata;
19425e950fafSDean Anderson 	struct s2255_vc *vc;
1943f5402007Ssensoray-dev 	dprintk(dev, 100, "buffer to user\n");
19445e950fafSDean Anderson 	vc = &dev->vc[dev->cc];
19455e950fafSDean Anderson 	idx = vc->cur_frame;
19465e950fafSDean Anderson 	frm = &vc->buffer.frame[idx];
19470aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
19480aa77f6cSMauro Carvalho Chehab 		int jj;
19490aa77f6cSMauro Carvalho Chehab 		unsigned int cc;
19500aa77f6cSMauro Carvalho Chehab 		__le32 *pdword; /*data from dsp is little endian */
19510aa77f6cSMauro Carvalho Chehab 		int payload;
19520aa77f6cSMauro Carvalho Chehab 		/* search for marker codes */
19530aa77f6cSMauro Carvalho Chehab 		pdata = (unsigned char *)pipe_info->transfer_buffer;
19540aa77f6cSMauro Carvalho Chehab 		pdword = (__le32 *)pdata;
19550aa77f6cSMauro Carvalho Chehab 		for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
19560aa77f6cSMauro Carvalho Chehab 			switch (*pdword) {
19570aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_FRAME:
1958f5402007Ssensoray-dev 				dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
1959f5402007Ssensoray-dev 					jj, pdata[0], pdata[1]);
19600aa77f6cSMauro Carvalho Chehab 				offset = jj + PREFIX_SIZE;
19610aa77f6cSMauro Carvalho Chehab 				bframe = 1;
19620aa77f6cSMauro Carvalho Chehab 				cc = le32_to_cpu(pdword[1]);
19630aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS) {
1964f5402007Ssensoray-dev 					dprintk(dev, 0,
19650aa77f6cSMauro Carvalho Chehab 						"bad channel\n");
19660aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
19670aa77f6cSMauro Carvalho Chehab 				}
19680aa77f6cSMauro Carvalho Chehab 				/* reverse it */
19690aa77f6cSMauro Carvalho Chehab 				dev->cc = G_chnmap[cc];
19705e950fafSDean Anderson 				vc = &dev->vc[dev->cc];
19710aa77f6cSMauro Carvalho Chehab 				payload =  le32_to_cpu(pdword[3]);
19725e950fafSDean Anderson 				if (payload > vc->req_image_size) {
19735e950fafSDean Anderson 					vc->bad_payload++;
19740aa77f6cSMauro Carvalho Chehab 					/* discard the bad frame */
19750aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
19760aa77f6cSMauro Carvalho Chehab 				}
19775e950fafSDean Anderson 				vc->pkt_size = payload;
19785e950fafSDean Anderson 				vc->jpg_size = le32_to_cpu(pdword[4]);
19790aa77f6cSMauro Carvalho Chehab 				break;
19800aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_RESPONSE:
19810aa77f6cSMauro Carvalho Chehab 
19820aa77f6cSMauro Carvalho Chehab 				pdata += DEF_USB_BLOCK;
19830aa77f6cSMauro Carvalho Chehab 				jj += DEF_USB_BLOCK;
19840aa77f6cSMauro Carvalho Chehab 				if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
19850aa77f6cSMauro Carvalho Chehab 					break;
19860aa77f6cSMauro Carvalho Chehab 				cc = G_chnmap[le32_to_cpu(pdword[1])];
19870aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS)
19880aa77f6cSMauro Carvalho Chehab 					break;
19895e950fafSDean Anderson 				vc = &dev->vc[cc];
19900aa77f6cSMauro Carvalho Chehab 				switch (pdword[2]) {
19910aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_SETMODE:
19920aa77f6cSMauro Carvalho Chehab 					/* check if channel valid */
19930aa77f6cSMauro Carvalho Chehab 					/* set mode ready */
19945e950fafSDean Anderson 					vc->setmode_ready = 1;
19955e950fafSDean Anderson 					wake_up(&vc->wait_setmode);
1996f5402007Ssensoray-dev 					dprintk(dev, 5, "setmode rdy %d\n", cc);
19970aa77f6cSMauro Carvalho Chehab 					break;
19980aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_FW:
19990aa77f6cSMauro Carvalho Chehab 					dev->chn_ready |= (1 << cc);
20000aa77f6cSMauro Carvalho Chehab 					if ((dev->chn_ready & 0x0f) != 0x0f)
20010aa77f6cSMauro Carvalho Chehab 						break;
20020aa77f6cSMauro Carvalho Chehab 					/* all channels ready */
2003f5402007Ssensoray-dev 					pr_info("s2255: fw loaded\n");
20040aa77f6cSMauro Carvalho Chehab 					atomic_set(&dev->fw_data->fw_state,
20050aa77f6cSMauro Carvalho Chehab 						   S2255_FW_SUCCESS);
20060aa77f6cSMauro Carvalho Chehab 					wake_up(&dev->fw_data->wait_fw);
20070aa77f6cSMauro Carvalho Chehab 					break;
20080aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_STATUS:
20095e950fafSDean Anderson 					vc->vidstatus = le32_to_cpu(pdword[3]);
20105e950fafSDean Anderson 					vc->vidstatus_ready = 1;
20115e950fafSDean Anderson 					wake_up(&vc->wait_vidstatus);
2012f5402007Ssensoray-dev 					dprintk(dev, 5, "vstat %x chan %d\n",
20130aa77f6cSMauro Carvalho Chehab 						le32_to_cpu(pdword[3]), cc);
20140aa77f6cSMauro Carvalho Chehab 					break;
20150aa77f6cSMauro Carvalho Chehab 				default:
2016f5402007Ssensoray-dev 					pr_info("s2255 unknown resp\n");
20170aa77f6cSMauro Carvalho Chehab 				}
20180aa77f6cSMauro Carvalho Chehab 			default:
20190aa77f6cSMauro Carvalho Chehab 				pdata++;
20200aa77f6cSMauro Carvalho Chehab 				break;
20210aa77f6cSMauro Carvalho Chehab 			}
20220aa77f6cSMauro Carvalho Chehab 			if (bframe)
20230aa77f6cSMauro Carvalho Chehab 				break;
20240aa77f6cSMauro Carvalho Chehab 		} /* for */
20250aa77f6cSMauro Carvalho Chehab 		if (!bframe)
20260aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
20270aa77f6cSMauro Carvalho Chehab 	}
20285e950fafSDean Anderson 	vc = &dev->vc[dev->cc];
20295e950fafSDean Anderson 	idx = vc->cur_frame;
20305e950fafSDean Anderson 	frm = &vc->buffer.frame[idx];
20310aa77f6cSMauro Carvalho Chehab 	/* search done.  now find out if should be acquiring on this channel */
20325e950fafSDean Anderson 	if (!vc->b_acquire) {
20330aa77f6cSMauro Carvalho Chehab 		/* we found a frame, but this channel is turned off */
20340aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
20350aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
20360aa77f6cSMauro Carvalho Chehab 	}
20370aa77f6cSMauro Carvalho Chehab 
20380aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
20390aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_FRAME;
20400aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
20410aa77f6cSMauro Carvalho Chehab 	}
20420aa77f6cSMauro Carvalho Chehab 
20430aa77f6cSMauro Carvalho Chehab 	/* skip the marker 512 bytes (and offset if out of sync) */
20440aa77f6cSMauro Carvalho Chehab 	psrc = (u8 *)pipe_info->transfer_buffer + offset;
20450aa77f6cSMauro Carvalho Chehab 
20460aa77f6cSMauro Carvalho Chehab 
20470aa77f6cSMauro Carvalho Chehab 	if (frm->lpvbits == NULL) {
2048f5402007Ssensoray-dev 		dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d",
20490aa77f6cSMauro Carvalho Chehab 			frm, dev, dev->cc, idx);
20500aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
20510aa77f6cSMauro Carvalho Chehab 	}
20520aa77f6cSMauro Carvalho Chehab 
20530aa77f6cSMauro Carvalho Chehab 	pdest = frm->lpvbits + frm->cur_size;
20540aa77f6cSMauro Carvalho Chehab 
20550aa77f6cSMauro Carvalho Chehab 	copy_size = (pipe_info->cur_transfer_size - offset);
20560aa77f6cSMauro Carvalho Chehab 
20575e950fafSDean Anderson 	size = vc->pkt_size - PREFIX_SIZE;
20580aa77f6cSMauro Carvalho Chehab 
20590aa77f6cSMauro Carvalho Chehab 	/* sanity check on pdest */
20605e950fafSDean Anderson 	if ((copy_size + frm->cur_size) < vc->req_image_size)
20610aa77f6cSMauro Carvalho Chehab 		memcpy(pdest, psrc, copy_size);
20620aa77f6cSMauro Carvalho Chehab 
20630aa77f6cSMauro Carvalho Chehab 	frm->cur_size += copy_size;
2064f5402007Ssensoray-dev 	dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size);
20650aa77f6cSMauro Carvalho Chehab 
20660aa77f6cSMauro Carvalho Chehab 	if (frm->cur_size >= size) {
2067f5402007Ssensoray-dev 		dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n",
20680aa77f6cSMauro Carvalho Chehab 			dev->cc, idx);
20695e950fafSDean Anderson 		vc->last_frame = vc->cur_frame;
20705e950fafSDean Anderson 		vc->cur_frame++;
20710aa77f6cSMauro Carvalho Chehab 		/* end of system frame ring buffer, start at zero */
20725e950fafSDean Anderson 		if ((vc->cur_frame == SYS_FRAMES) ||
20735e950fafSDean Anderson 		    (vc->cur_frame == vc->buffer.dwFrames))
20745e950fafSDean Anderson 			vc->cur_frame = 0;
20750aa77f6cSMauro Carvalho Chehab 		/* frame ready */
20765e950fafSDean Anderson 		if (vc->b_acquire)
20775e950fafSDean Anderson 			s2255_got_frame(vc, vc->jpg_size);
20785e950fafSDean Anderson 		vc->frame_count++;
20790aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
20800aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
20810aa77f6cSMauro Carvalho Chehab 
20820aa77f6cSMauro Carvalho Chehab 	}
20830aa77f6cSMauro Carvalho Chehab 	/* done successfully */
20840aa77f6cSMauro Carvalho Chehab 	return 0;
20850aa77f6cSMauro Carvalho Chehab }
20860aa77f6cSMauro Carvalho Chehab 
20870aa77f6cSMauro Carvalho Chehab static void s2255_read_video_callback(struct s2255_dev *dev,
20880aa77f6cSMauro Carvalho Chehab 				      struct s2255_pipeinfo *pipe_info)
20890aa77f6cSMauro Carvalho Chehab {
20900aa77f6cSMauro Carvalho Chehab 	int res;
2091f5402007Ssensoray-dev 	dprintk(dev, 50, "callback read video\n");
20920aa77f6cSMauro Carvalho Chehab 
20930aa77f6cSMauro Carvalho Chehab 	if (dev->cc >= MAX_CHANNELS) {
20940aa77f6cSMauro Carvalho Chehab 		dev->cc = 0;
20950aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid channel\n");
20960aa77f6cSMauro Carvalho Chehab 		return;
20970aa77f6cSMauro Carvalho Chehab 	}
20980aa77f6cSMauro Carvalho Chehab 	/* otherwise copy to the system buffers */
20990aa77f6cSMauro Carvalho Chehab 	res = save_frame(dev, pipe_info);
21000aa77f6cSMauro Carvalho Chehab 	if (res != 0)
2101f5402007Ssensoray-dev 		dprintk(dev, 4, "s2255: read callback failed\n");
21020aa77f6cSMauro Carvalho Chehab 
2103f5402007Ssensoray-dev 	dprintk(dev, 50, "callback read video done\n");
21040aa77f6cSMauro Carvalho Chehab 	return;
21050aa77f6cSMauro Carvalho Chehab }
21060aa77f6cSMauro Carvalho Chehab 
21070aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
21080aa77f6cSMauro Carvalho Chehab 			     u16 Index, u16 Value, void *TransferBuffer,
21090aa77f6cSMauro Carvalho Chehab 			     s32 TransferBufferLength, int bOut)
21100aa77f6cSMauro Carvalho Chehab {
21110aa77f6cSMauro Carvalho Chehab 	int r;
21120aa77f6cSMauro Carvalho Chehab 	if (!bOut) {
21130aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
21140aa77f6cSMauro Carvalho Chehab 				    Request,
21150aa77f6cSMauro Carvalho Chehab 				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
21160aa77f6cSMauro Carvalho Chehab 				    USB_DIR_IN,
21170aa77f6cSMauro Carvalho Chehab 				    Value, Index, TransferBuffer,
21180aa77f6cSMauro Carvalho Chehab 				    TransferBufferLength, HZ * 5);
21190aa77f6cSMauro Carvalho Chehab 	} else {
21200aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
21210aa77f6cSMauro Carvalho Chehab 				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
21220aa77f6cSMauro Carvalho Chehab 				    Value, Index, TransferBuffer,
21230aa77f6cSMauro Carvalho Chehab 				    TransferBufferLength, HZ * 5);
21240aa77f6cSMauro Carvalho Chehab 	}
21250aa77f6cSMauro Carvalho Chehab 	return r;
21260aa77f6cSMauro Carvalho Chehab }
21270aa77f6cSMauro Carvalho Chehab 
21280aa77f6cSMauro Carvalho Chehab /*
21290aa77f6cSMauro Carvalho Chehab  * retrieve FX2 firmware version. future use.
21300aa77f6cSMauro Carvalho Chehab  * @param dev pointer to device extension
21310aa77f6cSMauro Carvalho Chehab  * @return -1 for fail, else returns firmware version as an int(16 bits)
21320aa77f6cSMauro Carvalho Chehab  */
21330aa77f6cSMauro Carvalho Chehab static int s2255_get_fx2fw(struct s2255_dev *dev)
21340aa77f6cSMauro Carvalho Chehab {
21350aa77f6cSMauro Carvalho Chehab 	int fw;
21360aa77f6cSMauro Carvalho Chehab 	int ret;
21370aa77f6cSMauro Carvalho Chehab 	unsigned char transBuffer[64];
21380aa77f6cSMauro Carvalho Chehab 	ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
21390aa77f6cSMauro Carvalho Chehab 			       S2255_VR_IN);
21400aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
2141f5402007Ssensoray-dev 		dprintk(dev, 2, "get fw error: %x\n", ret);
21420aa77f6cSMauro Carvalho Chehab 	fw = transBuffer[0] + (transBuffer[1] << 8);
2143f5402007Ssensoray-dev 	dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
21440aa77f6cSMauro Carvalho Chehab 	return fw;
21450aa77f6cSMauro Carvalho Chehab }
21460aa77f6cSMauro Carvalho Chehab 
21470aa77f6cSMauro Carvalho Chehab /*
21480aa77f6cSMauro Carvalho Chehab  * Create the system ring buffer to copy frames into from the
21490aa77f6cSMauro Carvalho Chehab  * usb read pipe.
21500aa77f6cSMauro Carvalho Chehab  */
21515e950fafSDean Anderson static int s2255_create_sys_buffers(struct s2255_vc *vc)
21520aa77f6cSMauro Carvalho Chehab {
21530aa77f6cSMauro Carvalho Chehab 	unsigned long i;
21540aa77f6cSMauro Carvalho Chehab 	unsigned long reqsize;
21555e950fafSDean Anderson 	vc->buffer.dwFrames = SYS_FRAMES;
21560aa77f6cSMauro Carvalho Chehab 	/* always allocate maximum size(PAL) for system buffers */
21570aa77f6cSMauro Carvalho Chehab 	reqsize = SYS_FRAMES_MAXSIZE;
21580aa77f6cSMauro Carvalho Chehab 
21590aa77f6cSMauro Carvalho Chehab 	if (reqsize > SYS_FRAMES_MAXSIZE)
21600aa77f6cSMauro Carvalho Chehab 		reqsize = SYS_FRAMES_MAXSIZE;
21610aa77f6cSMauro Carvalho Chehab 
21620aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
21630aa77f6cSMauro Carvalho Chehab 		/* allocate the frames */
21645e950fafSDean Anderson 		vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
21655e950fafSDean Anderson 		vc->buffer.frame[i].size = reqsize;
21665e950fafSDean Anderson 		if (vc->buffer.frame[i].lpvbits == NULL) {
2167f5402007Ssensoray-dev 			pr_info("out of memory.  using less frames\n");
21685e950fafSDean Anderson 			vc->buffer.dwFrames = i;
21690aa77f6cSMauro Carvalho Chehab 			break;
21700aa77f6cSMauro Carvalho Chehab 		}
21710aa77f6cSMauro Carvalho Chehab 	}
21720aa77f6cSMauro Carvalho Chehab 
21730aa77f6cSMauro Carvalho Chehab 	/* make sure internal states are set */
21740aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
21755e950fafSDean Anderson 		vc->buffer.frame[i].ulState = 0;
21765e950fafSDean Anderson 		vc->buffer.frame[i].cur_size = 0;
21770aa77f6cSMauro Carvalho Chehab 	}
21780aa77f6cSMauro Carvalho Chehab 
21795e950fafSDean Anderson 	vc->cur_frame = 0;
21805e950fafSDean Anderson 	vc->last_frame = -1;
21810aa77f6cSMauro Carvalho Chehab 	return 0;
21820aa77f6cSMauro Carvalho Chehab }
21830aa77f6cSMauro Carvalho Chehab 
21845e950fafSDean Anderson static int s2255_release_sys_buffers(struct s2255_vc *vc)
21850aa77f6cSMauro Carvalho Chehab {
21860aa77f6cSMauro Carvalho Chehab 	unsigned long i;
21870aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
21885e950fafSDean Anderson 		if (vc->buffer.frame[i].lpvbits)
21895e950fafSDean Anderson 			vfree(vc->buffer.frame[i].lpvbits);
21905e950fafSDean Anderson 		vc->buffer.frame[i].lpvbits = NULL;
21910aa77f6cSMauro Carvalho Chehab 	}
21920aa77f6cSMauro Carvalho Chehab 	return 0;
21930aa77f6cSMauro Carvalho Chehab }
21940aa77f6cSMauro Carvalho Chehab 
21950aa77f6cSMauro Carvalho Chehab static int s2255_board_init(struct s2255_dev *dev)
21960aa77f6cSMauro Carvalho Chehab {
21970aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
21980aa77f6cSMauro Carvalho Chehab 	int fw_ver;
21990aa77f6cSMauro Carvalho Chehab 	int j;
22000aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
2201f5402007Ssensoray-dev 	dprintk(dev, 4, "board init: %p", dev);
22020aa77f6cSMauro Carvalho Chehab 	memset(pipe, 0, sizeof(*pipe));
22030aa77f6cSMauro Carvalho Chehab 	pipe->dev = dev;
22040aa77f6cSMauro Carvalho Chehab 	pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
22050aa77f6cSMauro Carvalho Chehab 	pipe->max_transfer_size = S2255_USB_XFER_SIZE;
22060aa77f6cSMauro Carvalho Chehab 
22070aa77f6cSMauro Carvalho Chehab 	pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
22080aa77f6cSMauro Carvalho Chehab 					GFP_KERNEL);
22090aa77f6cSMauro Carvalho Chehab 	if (pipe->transfer_buffer == NULL) {
2210f5402007Ssensoray-dev 		dprintk(dev, 1, "out of memory!\n");
22110aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
22120aa77f6cSMauro Carvalho Chehab 	}
22130aa77f6cSMauro Carvalho Chehab 	/* query the firmware */
22140aa77f6cSMauro Carvalho Chehab 	fw_ver = s2255_get_fx2fw(dev);
22150aa77f6cSMauro Carvalho Chehab 
2216f5402007Ssensoray-dev 	pr_info("s2255: usb firmware version %d.%d\n",
22170aa77f6cSMauro Carvalho Chehab 		(fw_ver >> 8) & 0xff,
22180aa77f6cSMauro Carvalho Chehab 		fw_ver & 0xff);
22190aa77f6cSMauro Carvalho Chehab 
22200aa77f6cSMauro Carvalho Chehab 	if (fw_ver < S2255_CUR_USB_FWVER)
2221f5402007Ssensoray-dev 		pr_info("s2255: newer USB firmware available\n");
22220aa77f6cSMauro Carvalho Chehab 
22230aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < MAX_CHANNELS; j++) {
22245e950fafSDean Anderson 		struct s2255_vc *vc = &dev->vc[j];
22255e950fafSDean Anderson 		vc->b_acquire = 0;
22265e950fafSDean Anderson 		vc->mode = mode_def;
22270aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 && j > 1)
22285e950fafSDean Anderson 			vc->mode.color |= (1 << 16);
22295e950fafSDean Anderson 		vc->jpegqual = S2255_DEF_JPEG_QUAL;
22305e950fafSDean Anderson 		vc->width = LINE_SZ_4CIFS_NTSC;
22315e950fafSDean Anderson 		vc->height = NUM_LINES_4CIFS_NTSC * 2;
22325e950fafSDean Anderson 		vc->std = V4L2_STD_NTSC_M;
22335e950fafSDean Anderson 		vc->fmt = &formats[0];
22345e950fafSDean Anderson 		vc->mode.restart = 1;
22355e950fafSDean Anderson 		vc->req_image_size = get_transfer_size(&mode_def);
22365e950fafSDean Anderson 		vc->frame_count = 0;
22370aa77f6cSMauro Carvalho Chehab 		/* create the system buffers */
22385e950fafSDean Anderson 		s2255_create_sys_buffers(vc);
22390aa77f6cSMauro Carvalho Chehab 	}
22400aa77f6cSMauro Carvalho Chehab 	/* start read pipe */
22410aa77f6cSMauro Carvalho Chehab 	s2255_start_readpipe(dev);
2242f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: success\n", __func__);
22430aa77f6cSMauro Carvalho Chehab 	return 0;
22440aa77f6cSMauro Carvalho Chehab }
22450aa77f6cSMauro Carvalho Chehab 
22460aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev)
22470aa77f6cSMauro Carvalho Chehab {
22480aa77f6cSMauro Carvalho Chehab 	u32 i;
2249f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: dev: %p", __func__,  dev);
22500aa77f6cSMauro Carvalho Chehab 
22510aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
22525e950fafSDean Anderson 		if (dev->vc[i].b_acquire)
22535e950fafSDean Anderson 			s2255_stop_acquire(&dev->vc[i]);
22540aa77f6cSMauro Carvalho Chehab 	}
22550aa77f6cSMauro Carvalho Chehab 	s2255_stop_readpipe(dev);
22560aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++)
22575e950fafSDean Anderson 		s2255_release_sys_buffers(&dev->vc[i]);
22580aa77f6cSMauro Carvalho Chehab 	/* release transfer buffer */
22590aa77f6cSMauro Carvalho Chehab 	kfree(dev->pipe.transfer_buffer);
22600aa77f6cSMauro Carvalho Chehab 	return 0;
22610aa77f6cSMauro Carvalho Chehab }
22620aa77f6cSMauro Carvalho Chehab 
22630aa77f6cSMauro Carvalho Chehab static void read_pipe_completion(struct urb *purb)
22640aa77f6cSMauro Carvalho Chehab {
22650aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info;
22660aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev;
22670aa77f6cSMauro Carvalho Chehab 	int status;
22680aa77f6cSMauro Carvalho Chehab 	int pipe;
22690aa77f6cSMauro Carvalho Chehab 	pipe_info = purb->context;
22700aa77f6cSMauro Carvalho Chehab 	if (pipe_info == NULL) {
22710aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
22720aa77f6cSMauro Carvalho Chehab 		return;
22730aa77f6cSMauro Carvalho Chehab 	}
22740aa77f6cSMauro Carvalho Chehab 	dev = pipe_info->dev;
22750aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
22760aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
22770aa77f6cSMauro Carvalho Chehab 		return;
22780aa77f6cSMauro Carvalho Chehab 	}
22790aa77f6cSMauro Carvalho Chehab 	status = purb->status;
22800aa77f6cSMauro Carvalho Chehab 	/* if shutting down, do not resubmit, exit immediately */
22810aa77f6cSMauro Carvalho Chehab 	if (status == -ESHUTDOWN) {
2282f5402007Ssensoray-dev 		dprintk(dev, 2, "%s: err shutdown\n", __func__);
22830aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
22840aa77f6cSMauro Carvalho Chehab 		return;
22850aa77f6cSMauro Carvalho Chehab 	}
22860aa77f6cSMauro Carvalho Chehab 
22870aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state == 0) {
2288f5402007Ssensoray-dev 		dprintk(dev, 2, "%s: exiting USB pipe", __func__);
22890aa77f6cSMauro Carvalho Chehab 		return;
22900aa77f6cSMauro Carvalho Chehab 	}
22910aa77f6cSMauro Carvalho Chehab 
22920aa77f6cSMauro Carvalho Chehab 	if (status == 0)
22930aa77f6cSMauro Carvalho Chehab 		s2255_read_video_callback(dev, pipe_info);
22940aa77f6cSMauro Carvalho Chehab 	else {
22950aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
2296f5402007Ssensoray-dev 		dprintk(dev, 1, "%s: failed URB %d\n", __func__, status);
22970aa77f6cSMauro Carvalho Chehab 	}
22980aa77f6cSMauro Carvalho Chehab 
22990aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
23000aa77f6cSMauro Carvalho Chehab 	/* reuse urb */
23010aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
23020aa77f6cSMauro Carvalho Chehab 			  pipe,
23030aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
23040aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
23050aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
23060aa77f6cSMauro Carvalho Chehab 
23070aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state != 0) {
2308f5402007Ssensoray-dev 		if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC))
23090aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev, "error submitting urb\n");
23100aa77f6cSMauro Carvalho Chehab 	} else {
2311f5402007Ssensoray-dev 		dprintk(dev, 2, "%s :complete state 0\n", __func__);
23120aa77f6cSMauro Carvalho Chehab 	}
23130aa77f6cSMauro Carvalho Chehab 	return;
23140aa77f6cSMauro Carvalho Chehab }
23150aa77f6cSMauro Carvalho Chehab 
23160aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev)
23170aa77f6cSMauro Carvalho Chehab {
23180aa77f6cSMauro Carvalho Chehab 	int pipe;
23190aa77f6cSMauro Carvalho Chehab 	int retval;
23200aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info = &dev->pipe;
23210aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
2322f5402007Ssensoray-dev 	dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint);
23230aa77f6cSMauro Carvalho Chehab 	pipe_info->state = 1;
23240aa77f6cSMauro Carvalho Chehab 	pipe_info->err_count = 0;
23250aa77f6cSMauro Carvalho Chehab 	pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
23260aa77f6cSMauro Carvalho Chehab 	if (!pipe_info->stream_urb) {
23270aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev,
23280aa77f6cSMauro Carvalho Chehab 			"ReadStream: Unable to alloc URB\n");
23290aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
23300aa77f6cSMauro Carvalho Chehab 	}
23310aa77f6cSMauro Carvalho Chehab 	/* transfer buffer allocated in board_init */
23320aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
23330aa77f6cSMauro Carvalho Chehab 			  pipe,
23340aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
23350aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
23360aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
23370aa77f6cSMauro Carvalho Chehab 	retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
23380aa77f6cSMauro Carvalho Chehab 	if (retval) {
2339f5402007Ssensoray-dev 		pr_err("s2255: start read pipe failed\n");
23400aa77f6cSMauro Carvalho Chehab 		return retval;
23410aa77f6cSMauro Carvalho Chehab 	}
23420aa77f6cSMauro Carvalho Chehab 	return 0;
23430aa77f6cSMauro Carvalho Chehab }
23440aa77f6cSMauro Carvalho Chehab 
23450aa77f6cSMauro Carvalho Chehab /* starts acquisition process */
23465e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc)
23470aa77f6cSMauro Carvalho Chehab {
23480aa77f6cSMauro Carvalho Chehab 	int res;
23490aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
23500aa77f6cSMauro Carvalho Chehab 	int j;
23515e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
235247d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
23530aa77f6cSMauro Carvalho Chehab 
235447d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
235547d8c881SDean Anderson 	chn_rev = G_chnmap[vc->idx];
23565e950fafSDean Anderson 	vc->last_frame = -1;
23575e950fafSDean Anderson 	vc->bad_payload = 0;
23585e950fafSDean Anderson 	vc->cur_frame = 0;
23590aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
23605e950fafSDean Anderson 		vc->buffer.frame[j].ulState = 0;
23615e950fafSDean Anderson 		vc->buffer.frame[j].cur_size = 0;
23620aa77f6cSMauro Carvalho Chehab 	}
23630aa77f6cSMauro Carvalho Chehab 
23640aa77f6cSMauro Carvalho Chehab 	/* send the start command */
236547d8c881SDean Anderson 	buffer[0] = IN_DATA_TOKEN;
236647d8c881SDean Anderson 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
236747d8c881SDean Anderson 	buffer[2] = CMD_START;
23680aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
23690aa77f6cSMauro Carvalho Chehab 	if (res != 0)
23700aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_START error\n");
23710aa77f6cSMauro Carvalho Chehab 
23725e950fafSDean Anderson 	dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res);
237347d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
23746a5b63b3SDean Anderson 	return res;
23750aa77f6cSMauro Carvalho Chehab }
23760aa77f6cSMauro Carvalho Chehab 
23775e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc)
23780aa77f6cSMauro Carvalho Chehab {
23790aa77f6cSMauro Carvalho Chehab 	int res;
23800aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
23815e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
238247d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
238347d8c881SDean Anderson 
238447d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
23855e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
23860aa77f6cSMauro Carvalho Chehab 	buffer = kzalloc(512, GFP_KERNEL);
23870aa77f6cSMauro Carvalho Chehab 	if (buffer == NULL) {
23880aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "out of mem\n");
23890aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
23900aa77f6cSMauro Carvalho Chehab 	}
23910aa77f6cSMauro Carvalho Chehab 	/* send the stop command */
239247d8c881SDean Anderson 	buffer[0] = IN_DATA_TOKEN;
239347d8c881SDean Anderson 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
239447d8c881SDean Anderson 	buffer[2] = CMD_STOP;
239547d8c881SDean Anderson 
23960aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
23970aa77f6cSMauro Carvalho Chehab 	if (res != 0)
23980aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_STOP error\n");
239947d8c881SDean Anderson 
24005e950fafSDean Anderson 	vc->b_acquire = 0;
24015e950fafSDean Anderson 	dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res);
240247d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
24030aa77f6cSMauro Carvalho Chehab 	return res;
24040aa77f6cSMauro Carvalho Chehab }
24050aa77f6cSMauro Carvalho Chehab 
24060aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev)
24070aa77f6cSMauro Carvalho Chehab {
24080aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
24090aa77f6cSMauro Carvalho Chehab 
24100aa77f6cSMauro Carvalho Chehab 	pipe->state = 0;
24110aa77f6cSMauro Carvalho Chehab 	if (pipe->stream_urb) {
24120aa77f6cSMauro Carvalho Chehab 		/* cancel urb */
24130aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(pipe->stream_urb);
24140aa77f6cSMauro Carvalho Chehab 		usb_free_urb(pipe->stream_urb);
24150aa77f6cSMauro Carvalho Chehab 		pipe->stream_urb = NULL;
24160aa77f6cSMauro Carvalho Chehab 	}
2417f5402007Ssensoray-dev 	dprintk(dev, 4, "%s", __func__);
24180aa77f6cSMauro Carvalho Chehab 	return;
24190aa77f6cSMauro Carvalho Chehab }
24200aa77f6cSMauro Carvalho Chehab 
24210aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset)
24220aa77f6cSMauro Carvalho Chehab {
24230aa77f6cSMauro Carvalho Chehab 	if (reset)
24240aa77f6cSMauro Carvalho Chehab 		s2255_reset_dsppower(dev);
24250aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_size = dev->fw_data->fw->size;
24260aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
24270aa77f6cSMauro Carvalho Chehab 	memcpy(dev->fw_data->pfw_data,
24280aa77f6cSMauro Carvalho Chehab 	       dev->fw_data->fw->data, CHUNK_SIZE);
24290aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_loaded = CHUNK_SIZE;
24300aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
24310aa77f6cSMauro Carvalho Chehab 			  usb_sndbulkpipe(dev->udev, 2),
24320aa77f6cSMauro Carvalho Chehab 			  dev->fw_data->pfw_data,
24330aa77f6cSMauro Carvalho Chehab 			  CHUNK_SIZE, s2255_fwchunk_complete,
24340aa77f6cSMauro Carvalho Chehab 			  dev->fw_data);
24350aa77f6cSMauro Carvalho Chehab 	mod_timer(&dev->timer, jiffies + HZ);
24360aa77f6cSMauro Carvalho Chehab }
24370aa77f6cSMauro Carvalho Chehab 
24380aa77f6cSMauro Carvalho Chehab /* standard usb probe function */
24390aa77f6cSMauro Carvalho Chehab static int s2255_probe(struct usb_interface *interface,
24400aa77f6cSMauro Carvalho Chehab 		       const struct usb_device_id *id)
24410aa77f6cSMauro Carvalho Chehab {
24420aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = NULL;
24430aa77f6cSMauro Carvalho Chehab 	struct usb_host_interface *iface_desc;
24440aa77f6cSMauro Carvalho Chehab 	struct usb_endpoint_descriptor *endpoint;
24450aa77f6cSMauro Carvalho Chehab 	int i;
24460aa77f6cSMauro Carvalho Chehab 	int retval = -ENOMEM;
24470aa77f6cSMauro Carvalho Chehab 	__le32 *pdata;
24480aa77f6cSMauro Carvalho Chehab 	int fw_size;
244947d8c881SDean Anderson 
24500aa77f6cSMauro Carvalho Chehab 	/* allocate memory for our device state and initialize it to zero */
24510aa77f6cSMauro Carvalho Chehab 	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
24520aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
24530aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&interface->dev, "out of memory\n");
24540aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
24550aa77f6cSMauro Carvalho Chehab 	}
245647d8c881SDean Anderson 
245747d8c881SDean Anderson 	dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
245847d8c881SDean Anderson 	if (dev->cmdbuf == NULL) {
245947d8c881SDean Anderson 		s2255_dev_err(&interface->dev, "out of memory\n");
246047d8c881SDean Anderson 		return -ENOMEM;
246147d8c881SDean Anderson 	}
246247d8c881SDean Anderson 
24630aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->num_channels, 0);
24640b84caabSHans Verkuil 	dev->pid = le16_to_cpu(id->idProduct);
24650aa77f6cSMauro Carvalho Chehab 	dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
24660aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data)
24670aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA1;
24680aa77f6cSMauro Carvalho Chehab 	mutex_init(&dev->lock);
246947d8c881SDean Anderson 	mutex_init(&dev->cmdlock);
24700aa77f6cSMauro Carvalho Chehab 	/* grab usb_device and save it */
24710aa77f6cSMauro Carvalho Chehab 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
24720aa77f6cSMauro Carvalho Chehab 	if (dev->udev == NULL) {
24730aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "null usb device\n");
24740aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
24750aa77f6cSMauro Carvalho Chehab 		goto errorUDEV;
24760aa77f6cSMauro Carvalho Chehab 	}
2477f5402007Ssensoray-dev 	dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
2478f5402007Ssensoray-dev 		dev, dev->udev, interface);
24790aa77f6cSMauro Carvalho Chehab 	dev->interface = interface;
24800aa77f6cSMauro Carvalho Chehab 	/* set up the endpoint information  */
24810aa77f6cSMauro Carvalho Chehab 	iface_desc = interface->cur_altsetting;
2482f5402007Ssensoray-dev 	dev_dbg(&interface->dev, "num EP: %d\n",
2483f5402007Ssensoray-dev 		iface_desc->desc.bNumEndpoints);
24840aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
24850aa77f6cSMauro Carvalho Chehab 		endpoint = &iface_desc->endpoint[i].desc;
24860aa77f6cSMauro Carvalho Chehab 		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
24870aa77f6cSMauro Carvalho Chehab 			/* we found the bulk in endpoint */
24880aa77f6cSMauro Carvalho Chehab 			dev->read_endpoint = endpoint->bEndpointAddress;
24890aa77f6cSMauro Carvalho Chehab 		}
24900aa77f6cSMauro Carvalho Chehab 	}
24910aa77f6cSMauro Carvalho Chehab 
24920aa77f6cSMauro Carvalho Chehab 	if (!dev->read_endpoint) {
24930aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
24940aa77f6cSMauro Carvalho Chehab 		goto errorEP;
24950aa77f6cSMauro Carvalho Chehab 	}
24960aa77f6cSMauro Carvalho Chehab 	init_timer(&dev->timer);
24970aa77f6cSMauro Carvalho Chehab 	dev->timer.function = s2255_timer;
24980aa77f6cSMauro Carvalho Chehab 	dev->timer.data = (unsigned long)dev->fw_data;
24990aa77f6cSMauro Carvalho Chehab 	init_waitqueue_head(&dev->fw_data->wait_fw);
25000aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
25015e950fafSDean Anderson 		struct s2255_vc *vc = &dev->vc[i];
25025e950fafSDean Anderson 		vc->idx = i;
25035e950fafSDean Anderson 		vc->dev = dev;
25045e950fafSDean Anderson 		init_waitqueue_head(&vc->wait_setmode);
25055e950fafSDean Anderson 		init_waitqueue_head(&vc->wait_vidstatus);
25060aa77f6cSMauro Carvalho Chehab 	}
25070aa77f6cSMauro Carvalho Chehab 
25080aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
25090aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data->fw_urb) {
25100aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "out of memory!\n");
25110aa77f6cSMauro Carvalho Chehab 		goto errorFWURB;
25120aa77f6cSMauro Carvalho Chehab 	}
25130aa77f6cSMauro Carvalho Chehab 
25140aa77f6cSMauro Carvalho Chehab 	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
25150aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data->pfw_data) {
25160aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "out of memory!\n");
25170aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA2;
25180aa77f6cSMauro Carvalho Chehab 	}
25190aa77f6cSMauro Carvalho Chehab 	/* load the first chunk */
25200aa77f6cSMauro Carvalho Chehab 	if (request_firmware(&dev->fw_data->fw,
25210aa77f6cSMauro Carvalho Chehab 			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
2522f5402007Ssensoray-dev 		dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n");
25230aa77f6cSMauro Carvalho Chehab 		goto errorREQFW;
25240aa77f6cSMauro Carvalho Chehab 	}
25250aa77f6cSMauro Carvalho Chehab 	/* check the firmware is valid */
25260aa77f6cSMauro Carvalho Chehab 	fw_size = dev->fw_data->fw->size;
25270aa77f6cSMauro Carvalho Chehab 	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
25280aa77f6cSMauro Carvalho Chehab 
25290aa77f6cSMauro Carvalho Chehab 	if (*pdata != S2255_FW_MARKER) {
2530f5402007Ssensoray-dev 		dev_err(&interface->dev, "Firmware invalid.\n");
25310aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
25320aa77f6cSMauro Carvalho Chehab 		goto errorFWMARKER;
25330aa77f6cSMauro Carvalho Chehab 	} else {
25340aa77f6cSMauro Carvalho Chehab 		/* make sure firmware is the latest */
25350aa77f6cSMauro Carvalho Chehab 		__le32 *pRel;
25360aa77f6cSMauro Carvalho Chehab 		pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
2537f5402007Ssensoray-dev 		pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
25380aa77f6cSMauro Carvalho Chehab 		dev->dsp_fw_ver = le32_to_cpu(*pRel);
25390aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
2540f5402007Ssensoray-dev 			pr_info("s2255: f2255usb.bin out of date.\n");
25410aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 &&
25420aa77f6cSMauro Carvalho Chehab 				dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
2543f5402007Ssensoray-dev 			pr_warn("2257 needs firmware %d or above.\n",
2544f5402007Ssensoray-dev 				S2255_MIN_DSP_COLORFILTER);
25450aa77f6cSMauro Carvalho Chehab 	}
25460aa77f6cSMauro Carvalho Chehab 	usb_reset_device(dev->udev);
25470aa77f6cSMauro Carvalho Chehab 	/* load 2255 board specific */
25480aa77f6cSMauro Carvalho Chehab 	retval = s2255_board_init(dev);
25490aa77f6cSMauro Carvalho Chehab 	if (retval)
25500aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
25510aa77f6cSMauro Carvalho Chehab 	spin_lock_init(&dev->slock);
25520aa77f6cSMauro Carvalho Chehab 	s2255_fwload_start(dev, 0);
25530aa77f6cSMauro Carvalho Chehab 	/* loads v4l specific */
25540aa77f6cSMauro Carvalho Chehab 	retval = s2255_probe_v4l(dev);
25550aa77f6cSMauro Carvalho Chehab 	if (retval)
25560aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
25570aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "Sensoray 2255 detected\n");
25580aa77f6cSMauro Carvalho Chehab 	return 0;
25590aa77f6cSMauro Carvalho Chehab errorBOARDINIT:
25600aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
25610aa77f6cSMauro Carvalho Chehab errorFWMARKER:
25620aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
25630aa77f6cSMauro Carvalho Chehab errorREQFW:
25640aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
25650aa77f6cSMauro Carvalho Chehab errorFWDATA2:
25660aa77f6cSMauro Carvalho Chehab 	usb_free_urb(dev->fw_data->fw_urb);
25670aa77f6cSMauro Carvalho Chehab errorFWURB:
25680aa77f6cSMauro Carvalho Chehab 	del_timer(&dev->timer);
25690aa77f6cSMauro Carvalho Chehab errorEP:
25700aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
25710aa77f6cSMauro Carvalho Chehab errorUDEV:
25720aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
25730aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
25740aa77f6cSMauro Carvalho Chehab errorFWDATA1:
257547d8c881SDean Anderson 	kfree(dev->cmdbuf);
25760aa77f6cSMauro Carvalho Chehab 	kfree(dev);
2577f5402007Ssensoray-dev 	pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval);
25780aa77f6cSMauro Carvalho Chehab 	return retval;
25790aa77f6cSMauro Carvalho Chehab }
25800aa77f6cSMauro Carvalho Chehab 
25810aa77f6cSMauro Carvalho Chehab /* disconnect routine. when board is removed physically or with rmmod */
25820aa77f6cSMauro Carvalho Chehab static void s2255_disconnect(struct usb_interface *interface)
25830aa77f6cSMauro Carvalho Chehab {
25840aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
25850aa77f6cSMauro Carvalho Chehab 	int i;
25860aa77f6cSMauro Carvalho Chehab 	int channels = atomic_read(&dev->num_channels);
25870aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
25880aa77f6cSMauro Carvalho Chehab 	v4l2_device_disconnect(&dev->v4l2_dev);
25890aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
25900aa77f6cSMauro Carvalho Chehab 	/*see comments in the uvc_driver.c usb disconnect function */
25910aa77f6cSMauro Carvalho Chehab 	atomic_inc(&dev->num_channels);
25920aa77f6cSMauro Carvalho Chehab 	/* unregister each video device. */
25930aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < channels; i++)
25945e950fafSDean Anderson 		video_unregister_device(&dev->vc[i].vdev);
25950aa77f6cSMauro Carvalho Chehab 	/* wake up any of our timers */
25960aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
25970aa77f6cSMauro Carvalho Chehab 	wake_up(&dev->fw_data->wait_fw);
25980aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
25995e950fafSDean Anderson 		dev->vc[i].setmode_ready = 1;
26005e950fafSDean Anderson 		wake_up(&dev->vc[i].wait_setmode);
26015e950fafSDean Anderson 		dev->vc[i].vidstatus_ready = 1;
26025e950fafSDean Anderson 		wake_up(&dev->vc[i].wait_vidstatus);
26030aa77f6cSMauro Carvalho Chehab 	}
26040aa77f6cSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->num_channels))
26050aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
26060aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "%s\n", __func__);
26070aa77f6cSMauro Carvalho Chehab }
26080aa77f6cSMauro Carvalho Chehab 
26090aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver = {
26100aa77f6cSMauro Carvalho Chehab 	.name = S2255_DRIVER_NAME,
26110aa77f6cSMauro Carvalho Chehab 	.probe = s2255_probe,
26120aa77f6cSMauro Carvalho Chehab 	.disconnect = s2255_disconnect,
26130aa77f6cSMauro Carvalho Chehab 	.id_table = s2255_table,
26140aa77f6cSMauro Carvalho Chehab };
26150aa77f6cSMauro Carvalho Chehab 
26160aa77f6cSMauro Carvalho Chehab module_usb_driver(s2255_driver);
26170aa77f6cSMauro Carvalho Chehab 
26180aa77f6cSMauro Carvalho Chehab MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
26190aa77f6cSMauro Carvalho Chehab MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
26200aa77f6cSMauro Carvalho Chehab MODULE_LICENSE("GPL");
26210aa77f6cSMauro Carvalho Chehab MODULE_VERSION(S2255_VERSION);
26220aa77f6cSMauro Carvalho Chehab MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
2623