xref: /openbmc/linux/drivers/media/usb/s2255/s2255drv.c (revision db65c49e442cf9c9d9dc950f67daf109609f982a)
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>
482d700715SJunghak Sung #include <media/videobuf2-v4l2.h>
49340a30c5Ssensoray-dev #include <media/videobuf2-vmalloc.h>
500aa77f6cSMauro Carvalho Chehab #include <media/v4l2-common.h>
510aa77f6cSMauro Carvalho Chehab #include <media/v4l2-device.h>
520aa77f6cSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
53192f1e78SHans Verkuil #include <media/v4l2-ctrls.h>
5444d06d82SHans Verkuil #include <media/v4l2-event.h>
550aa77f6cSMauro Carvalho Chehab 
56340a30c5Ssensoray-dev #define S2255_VERSION		"1.25.1"
570aa77f6cSMauro Carvalho Chehab #define FIRMWARE_FILE_NAME "f2255usb.bin"
580aa77f6cSMauro Carvalho Chehab 
590aa77f6cSMauro Carvalho Chehab /* default JPEG quality */
600aa77f6cSMauro Carvalho Chehab #define S2255_DEF_JPEG_QUAL     50
610aa77f6cSMauro Carvalho Chehab /* vendor request in */
620aa77f6cSMauro Carvalho Chehab #define S2255_VR_IN		0
630aa77f6cSMauro Carvalho Chehab /* vendor request out */
640aa77f6cSMauro Carvalho Chehab #define S2255_VR_OUT		1
650aa77f6cSMauro Carvalho Chehab /* firmware query */
660aa77f6cSMauro Carvalho Chehab #define S2255_VR_FW		0x30
670aa77f6cSMauro Carvalho Chehab /* USB endpoint number for configuring the device */
680aa77f6cSMauro Carvalho Chehab #define S2255_CONFIG_EP         2
690aa77f6cSMauro Carvalho Chehab /* maximum time for DSP to start responding after last FW word loaded(ms) */
700aa77f6cSMauro Carvalho Chehab #define S2255_DSP_BOOTTIME      800
710aa77f6cSMauro Carvalho Chehab /* maximum time to wait for firmware to load (ms) */
720aa77f6cSMauro Carvalho Chehab #define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
739da62eb0SDean Anderson #define S2255_MIN_BUFS          2
740aa77f6cSMauro Carvalho Chehab #define S2255_SETMODE_TIMEOUT   500
750aa77f6cSMauro Carvalho Chehab #define S2255_VIDSTATUS_TIMEOUT 350
760aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_FRAME	cpu_to_le32(0x2255DA4AL)
770aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_RESPONSE	cpu_to_le32(0x2255ACACL)
780aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_SETMODE  cpu_to_le32(0x01)
790aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_FW       cpu_to_le32(0x10)
800aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_STATUS   cpu_to_le32(0x20)
810aa77f6cSMauro Carvalho Chehab #define S2255_USB_XFER_SIZE	(16 * 1024)
820aa77f6cSMauro Carvalho Chehab #define MAX_CHANNELS		4
830aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES		4
840aa77f6cSMauro Carvalho Chehab /* maximum size is PAL full size plus room for the marker header(s) */
850aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
860aa77f6cSMauro Carvalho Chehab #define DEF_USB_BLOCK		S2255_USB_XFER_SIZE
870aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_NTSC	640
880aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_NTSC	640
890aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_NTSC	320
900aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_PAL	704
910aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_PAL	704
920aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_PAL	352
930aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_NTSC	240
940aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_NTSC	240
950aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_NTSC	240
960aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_PAL	288
970aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_PAL	288
980aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_PAL	288
990aa77f6cSMauro Carvalho Chehab #define LINE_SZ_DEF		640
1000aa77f6cSMauro Carvalho Chehab #define NUM_LINES_DEF		240
1010aa77f6cSMauro Carvalho Chehab 
1020aa77f6cSMauro Carvalho Chehab 
1030aa77f6cSMauro Carvalho Chehab /* predefined settings */
1040aa77f6cSMauro Carvalho Chehab #define FORMAT_NTSC	1
1050aa77f6cSMauro Carvalho Chehab #define FORMAT_PAL	2
1060aa77f6cSMauro Carvalho Chehab 
1070aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
1080aa77f6cSMauro Carvalho Chehab #define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
1090aa77f6cSMauro Carvalho Chehab #define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
1100aa77f6cSMauro Carvalho Chehab /* SCALE_4CIFSI is the 2 fields interpolated into one */
1110aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFSI	4	/* 640x480(NTSC) or 704x576(PAL) high quality */
1120aa77f6cSMauro Carvalho Chehab 
1130aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPL	1	/* YUV planar */
1140aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPK	2	/* YUV packed */
1150aa77f6cSMauro Carvalho Chehab #define COLOR_Y8	4	/* monochrome */
1160aa77f6cSMauro Carvalho Chehab #define COLOR_JPG       5       /* JPEG */
1170aa77f6cSMauro Carvalho Chehab 
1180aa77f6cSMauro Carvalho Chehab #define MASK_COLOR       0x000000ff
1190aa77f6cSMauro Carvalho Chehab #define MASK_JPG_QUALITY 0x0000ff00
1200aa77f6cSMauro Carvalho Chehab #define MASK_INPUT_TYPE  0x000f0000
1210aa77f6cSMauro Carvalho Chehab /* frame decimation. */
1220aa77f6cSMauro Carvalho Chehab #define FDEC_1		1	/* capture every frame. default */
1230aa77f6cSMauro Carvalho Chehab #define FDEC_2		2	/* capture every 2nd frame */
1240aa77f6cSMauro Carvalho Chehab #define FDEC_3		3	/* capture every 3rd frame */
1250aa77f6cSMauro Carvalho Chehab #define FDEC_5		5	/* capture every 5th frame */
1260aa77f6cSMauro Carvalho Chehab 
1270aa77f6cSMauro Carvalho Chehab /*-------------------------------------------------------
1280aa77f6cSMauro Carvalho Chehab  * Default mode parameters.
1290aa77f6cSMauro Carvalho Chehab  *-------------------------------------------------------*/
1300aa77f6cSMauro Carvalho Chehab #define DEF_SCALE	SCALE_4CIFS
1310aa77f6cSMauro Carvalho Chehab #define DEF_COLOR	COLOR_YUVPL
1320aa77f6cSMauro Carvalho Chehab #define DEF_FDEC	FDEC_1
1330aa77f6cSMauro Carvalho Chehab #define DEF_BRIGHT	0
1340aa77f6cSMauro Carvalho Chehab #define DEF_CONTRAST	0x5c
1350aa77f6cSMauro Carvalho Chehab #define DEF_SATURATION	0x80
1360aa77f6cSMauro Carvalho Chehab #define DEF_HUE		0
1370aa77f6cSMauro Carvalho Chehab 
1380aa77f6cSMauro Carvalho Chehab /* usb config commands */
1390aa77f6cSMauro Carvalho Chehab #define IN_DATA_TOKEN	cpu_to_le32(0x2255c0de)
1400aa77f6cSMauro Carvalho Chehab #define CMD_2255	0xc2255000
1410aa77f6cSMauro Carvalho Chehab #define CMD_SET_MODE	cpu_to_le32((CMD_2255 | 0x10))
1420aa77f6cSMauro Carvalho Chehab #define CMD_START	cpu_to_le32((CMD_2255 | 0x20))
1430aa77f6cSMauro Carvalho Chehab #define CMD_STOP	cpu_to_le32((CMD_2255 | 0x30))
1440aa77f6cSMauro Carvalho Chehab #define CMD_STATUS	cpu_to_le32((CMD_2255 | 0x40))
1450aa77f6cSMauro Carvalho Chehab 
1460aa77f6cSMauro Carvalho Chehab struct s2255_mode {
1470aa77f6cSMauro Carvalho Chehab 	u32 format;	/* input video format (NTSC, PAL) */
1480aa77f6cSMauro Carvalho Chehab 	u32 scale;	/* output video scale */
1490aa77f6cSMauro Carvalho Chehab 	u32 color;	/* output video color format */
1500aa77f6cSMauro Carvalho Chehab 	u32 fdec;	/* frame decimation */
1510aa77f6cSMauro Carvalho Chehab 	u32 bright;	/* brightness */
1520aa77f6cSMauro Carvalho Chehab 	u32 contrast;	/* contrast */
1530aa77f6cSMauro Carvalho Chehab 	u32 saturation;	/* saturation */
1540aa77f6cSMauro Carvalho Chehab 	u32 hue;	/* hue (NTSC only)*/
1550aa77f6cSMauro Carvalho Chehab 	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
1560aa77f6cSMauro Carvalho Chehab 	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
1570aa77f6cSMauro Carvalho Chehab 	u32 restart;	/* if DSP requires restart */
1580aa77f6cSMauro Carvalho Chehab };
1590aa77f6cSMauro Carvalho Chehab 
1600aa77f6cSMauro Carvalho Chehab 
1610aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE		0
1620aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME	1
1630aa77f6cSMauro Carvalho Chehab 
1640aa77f6cSMauro Carvalho Chehab /* frame structure */
1650aa77f6cSMauro Carvalho Chehab struct s2255_framei {
1660aa77f6cSMauro Carvalho Chehab 	unsigned long size;
1670aa77f6cSMauro Carvalho Chehab 	unsigned long ulState;	/* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
1680aa77f6cSMauro Carvalho Chehab 	void *lpvbits;		/* image data */
1690aa77f6cSMauro Carvalho Chehab 	unsigned long cur_size;	/* current data copied to it */
1700aa77f6cSMauro Carvalho Chehab };
1710aa77f6cSMauro Carvalho Chehab 
1720aa77f6cSMauro Carvalho Chehab /* image buffer structure */
1730aa77f6cSMauro Carvalho Chehab struct s2255_bufferi {
1740aa77f6cSMauro Carvalho Chehab 	unsigned long dwFrames;			/* number of frames in buffer */
1750aa77f6cSMauro Carvalho Chehab 	struct s2255_framei frame[SYS_FRAMES];	/* array of FRAME structures */
1760aa77f6cSMauro Carvalho Chehab };
1770aa77f6cSMauro Carvalho Chehab 
1780aa77f6cSMauro Carvalho Chehab #define DEF_MODEI_NTSC_CONT	{FORMAT_NTSC, DEF_SCALE, DEF_COLOR,	\
1790aa77f6cSMauro Carvalho Chehab 			DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
1800aa77f6cSMauro Carvalho Chehab 			DEF_HUE, 0, DEF_USB_BLOCK, 0}
1810aa77f6cSMauro Carvalho Chehab 
1820aa77f6cSMauro Carvalho Chehab /* for firmware loading, fw_state */
1830aa77f6cSMauro Carvalho Chehab #define S2255_FW_NOTLOADED	0
1840aa77f6cSMauro Carvalho Chehab #define S2255_FW_LOADED_DSPWAIT	1
1850aa77f6cSMauro Carvalho Chehab #define S2255_FW_SUCCESS	2
1860aa77f6cSMauro Carvalho Chehab #define S2255_FW_FAILED		3
1870aa77f6cSMauro Carvalho Chehab #define S2255_FW_DISCONNECTING  4
1880aa77f6cSMauro Carvalho Chehab #define S2255_FW_MARKER		cpu_to_le32(0x22552f2f)
1890aa77f6cSMauro Carvalho Chehab /* 2255 read states */
1900aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE         0
1910aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME        1
1920aa77f6cSMauro Carvalho Chehab struct s2255_fw {
1930aa77f6cSMauro Carvalho Chehab 	int		      fw_loaded;
1940aa77f6cSMauro Carvalho Chehab 	int		      fw_size;
1950aa77f6cSMauro Carvalho Chehab 	struct urb	      *fw_urb;
1960aa77f6cSMauro Carvalho Chehab 	atomic_t	      fw_state;
1970aa77f6cSMauro Carvalho Chehab 	void		      *pfw_data;
1980aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t     wait_fw;
1990aa77f6cSMauro Carvalho Chehab 	const struct firmware *fw;
2000aa77f6cSMauro Carvalho Chehab };
2010aa77f6cSMauro Carvalho Chehab 
2020aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo {
2030aa77f6cSMauro Carvalho Chehab 	u32 max_transfer_size;
2040aa77f6cSMauro Carvalho Chehab 	u32 cur_transfer_size;
2050aa77f6cSMauro Carvalho Chehab 	u8 *transfer_buffer;
2060aa77f6cSMauro Carvalho Chehab 	u32 state;
2070aa77f6cSMauro Carvalho Chehab 	void *stream_urb;
2080aa77f6cSMauro Carvalho Chehab 	void *dev;	/* back pointer to s2255_dev struct*/
2090aa77f6cSMauro Carvalho Chehab 	u32 err_count;
2100aa77f6cSMauro Carvalho Chehab 	u32 idx;
2110aa77f6cSMauro Carvalho Chehab };
2120aa77f6cSMauro Carvalho Chehab 
2130aa77f6cSMauro Carvalho Chehab struct s2255_fmt; /*forward declaration */
2140aa77f6cSMauro Carvalho Chehab struct s2255_dev;
2150aa77f6cSMauro Carvalho Chehab 
2165e950fafSDean Anderson /* 2255 video channel */
2175e950fafSDean Anderson struct s2255_vc {
218f5402007Ssensoray-dev 	struct s2255_dev        *dev;
2190aa77f6cSMauro Carvalho Chehab 	struct video_device	vdev;
220192f1e78SHans Verkuil 	struct v4l2_ctrl_handler hdl;
2217041dec7SHans Verkuil 	struct v4l2_ctrl	*jpegqual_ctrl;
2220aa77f6cSMauro Carvalho Chehab 	int			resources;
223d86c6a8cSDean Anderson 	struct list_head        buf_list;
2240aa77f6cSMauro Carvalho Chehab 	struct s2255_bufferi	buffer;
2250aa77f6cSMauro Carvalho Chehab 	struct s2255_mode	mode;
226469af77aSHans Verkuil 	v4l2_std_id		std;
2270aa77f6cSMauro Carvalho Chehab 	/* jpeg compression */
2287041dec7SHans Verkuil 	unsigned		jpegqual;
2290aa77f6cSMauro Carvalho Chehab 	/* capture parameters (for high quality mode full size) */
2300aa77f6cSMauro Carvalho Chehab 	struct v4l2_captureparm cap_parm;
2310aa77f6cSMauro Carvalho Chehab 	int			cur_frame;
2320aa77f6cSMauro Carvalho Chehab 	int			last_frame;
2330aa77f6cSMauro Carvalho Chehab 	/* allocated image size */
2340aa77f6cSMauro Carvalho Chehab 	unsigned long		req_image_size;
2350aa77f6cSMauro Carvalho Chehab 	/* received packet size */
2360aa77f6cSMauro Carvalho Chehab 	unsigned long		pkt_size;
2370aa77f6cSMauro Carvalho Chehab 	int			bad_payload;
2380aa77f6cSMauro Carvalho Chehab 	unsigned long		frame_count;
2390aa77f6cSMauro Carvalho Chehab 	/* if JPEG image */
2400aa77f6cSMauro Carvalho Chehab 	int                     jpg_size;
2410aa77f6cSMauro Carvalho Chehab 	/* if channel configured to default state */
2420aa77f6cSMauro Carvalho Chehab 	int                     configured;
2430aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_setmode;
2440aa77f6cSMauro Carvalho Chehab 	int                     setmode_ready;
2450aa77f6cSMauro Carvalho Chehab 	/* video status items */
2460aa77f6cSMauro Carvalho Chehab 	int                     vidstatus;
2470aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_vidstatus;
2480aa77f6cSMauro Carvalho Chehab 	int                     vidstatus_ready;
2490aa77f6cSMauro Carvalho Chehab 	unsigned int		width;
2500aa77f6cSMauro Carvalho Chehab 	unsigned int		height;
251340a30c5Ssensoray-dev 	enum v4l2_field         field;
2520aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt	*fmt;
2530aa77f6cSMauro Carvalho Chehab 	int idx; /* channel number on device, 0-3 */
254340a30c5Ssensoray-dev 	struct vb2_queue vb_vidq;
255340a30c5Ssensoray-dev 	struct mutex vb_lock; /* streaming lock */
256340a30c5Ssensoray-dev 	spinlock_t qlock;
2570aa77f6cSMauro Carvalho Chehab };
2580aa77f6cSMauro Carvalho Chehab 
2590aa77f6cSMauro Carvalho Chehab 
2600aa77f6cSMauro Carvalho Chehab struct s2255_dev {
2615e950fafSDean Anderson 	struct s2255_vc         vc[MAX_CHANNELS];
2620aa77f6cSMauro Carvalho Chehab 	struct v4l2_device      v4l2_dev;
2630aa77f6cSMauro Carvalho Chehab 	atomic_t                num_channels;
2640aa77f6cSMauro Carvalho Chehab 	int			frames;
2650aa77f6cSMauro Carvalho Chehab 	struct mutex		lock;	/* channels[].vdev.lock */
26647d8c881SDean Anderson 	struct mutex		cmdlock; /* protects cmdbuf */
2670aa77f6cSMauro Carvalho Chehab 	struct usb_device	*udev;
2680aa77f6cSMauro Carvalho Chehab 	struct usb_interface	*interface;
2690aa77f6cSMauro Carvalho Chehab 	u8			read_endpoint;
2700aa77f6cSMauro Carvalho Chehab 	struct timer_list	timer;
2710aa77f6cSMauro Carvalho Chehab 	struct s2255_fw	*fw_data;
2720aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo	pipe;
2730aa77f6cSMauro Carvalho Chehab 	u32			cc;	/* current channel */
2740aa77f6cSMauro Carvalho Chehab 	int			frame_ready;
2750aa77f6cSMauro Carvalho Chehab 	int                     chn_ready;
2760aa77f6cSMauro Carvalho Chehab 	/* dsp firmware version (f2255usb.bin) */
2770aa77f6cSMauro Carvalho Chehab 	int                     dsp_fw_ver;
2780aa77f6cSMauro Carvalho Chehab 	u16                     pid; /* product id */
27947d8c881SDean Anderson #define S2255_CMDBUF_SIZE 512
28047d8c881SDean Anderson 	__le32                  *cmdbuf;
2810aa77f6cSMauro Carvalho Chehab };
2820aa77f6cSMauro Carvalho Chehab 
2830aa77f6cSMauro Carvalho Chehab static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
2840aa77f6cSMauro Carvalho Chehab {
2850aa77f6cSMauro Carvalho Chehab 	return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
2860aa77f6cSMauro Carvalho Chehab }
2870aa77f6cSMauro Carvalho Chehab 
2880aa77f6cSMauro Carvalho Chehab struct s2255_fmt {
2890aa77f6cSMauro Carvalho Chehab 	char *name;
2900aa77f6cSMauro Carvalho Chehab 	u32 fourcc;
2910aa77f6cSMauro Carvalho Chehab 	int depth;
2920aa77f6cSMauro Carvalho Chehab };
2930aa77f6cSMauro Carvalho Chehab 
2940aa77f6cSMauro Carvalho Chehab /* buffer for one video frame */
2950aa77f6cSMauro Carvalho Chehab struct s2255_buffer {
2960aa77f6cSMauro Carvalho Chehab 	/* common v4l buffer stuff -- must be first */
2972d700715SJunghak Sung 	struct vb2_v4l2_buffer vb;
298340a30c5Ssensoray-dev 	struct list_head list;
2990aa77f6cSMauro Carvalho Chehab };
3000aa77f6cSMauro Carvalho Chehab 
3010aa77f6cSMauro Carvalho Chehab 
3020aa77f6cSMauro Carvalho Chehab /* current cypress EEPROM firmware version */
3030aa77f6cSMauro Carvalho Chehab #define S2255_CUR_USB_FWVER	((3 << 8) | 12)
3040aa77f6cSMauro Carvalho Chehab /* current DSP FW version */
3050aa77f6cSMauro Carvalho Chehab #define S2255_CUR_DSP_FWVER     10104
3060aa77f6cSMauro Carvalho Chehab /* Need DSP version 5+ for video status feature */
3070aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_STATUS      5
3080aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_COLORFILTER 8
309469af77aSHans Verkuil #define S2255_NORMS		(V4L2_STD_ALL)
3100aa77f6cSMauro Carvalho Chehab 
3110aa77f6cSMauro Carvalho Chehab /* private V4L2 controls */
3120aa77f6cSMauro Carvalho Chehab 
3130aa77f6cSMauro Carvalho Chehab /*
3140aa77f6cSMauro Carvalho Chehab  * The following chart displays how COLORFILTER should be set
3150aa77f6cSMauro Carvalho Chehab  *  =========================================================
3160aa77f6cSMauro Carvalho Chehab  *  =     fourcc              =     COLORFILTER             =
3170aa77f6cSMauro Carvalho Chehab  *  =                         ===============================
3180aa77f6cSMauro Carvalho Chehab  *  =                         =   0             =    1      =
3190aa77f6cSMauro Carvalho Chehab  *  =========================================================
3200aa77f6cSMauro Carvalho Chehab  *  =  V4L2_PIX_FMT_GREY(Y8)  = monochrome from = monochrome=
3210aa77f6cSMauro Carvalho Chehab  *  =                         = s-video or      = composite =
3220aa77f6cSMauro Carvalho Chehab  *  =                         = B/W camera      = input     =
3230aa77f6cSMauro Carvalho Chehab  *  =========================================================
3240aa77f6cSMauro Carvalho Chehab  *  =    other                = color, svideo   = color,    =
3250aa77f6cSMauro Carvalho Chehab  *  =                         =                 = composite =
3260aa77f6cSMauro Carvalho Chehab  *  =========================================================
3270aa77f6cSMauro Carvalho Chehab  *
3280aa77f6cSMauro Carvalho Chehab  * Notes:
3290aa77f6cSMauro Carvalho Chehab  *   channels 0-3 on 2255 are composite
3300aa77f6cSMauro Carvalho Chehab  *   channels 0-1 on 2257 are composite, 2-3 are s-video
3310aa77f6cSMauro Carvalho Chehab  * If COLORFILTER is 0 with a composite color camera connected,
3320aa77f6cSMauro Carvalho Chehab  * the output will appear monochrome but hatching
3330aa77f6cSMauro Carvalho Chehab  * will occur.
3340aa77f6cSMauro Carvalho Chehab  * COLORFILTER is different from "color killer" and "color effects"
3350aa77f6cSMauro Carvalho Chehab  * for reasons above.
3360aa77f6cSMauro Carvalho Chehab  */
3370aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_ON  1
3380aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_OFF 0
339192f1e78SHans Verkuil #define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0)
3400aa77f6cSMauro Carvalho Chehab 
3410aa77f6cSMauro Carvalho Chehab /* frame prefix size (sent once every frame) */
3420aa77f6cSMauro Carvalho Chehab #define PREFIX_SIZE		512
3430aa77f6cSMauro Carvalho Chehab 
3440aa77f6cSMauro Carvalho Chehab /* Channels on box are in reverse order */
3450aa77f6cSMauro Carvalho Chehab static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
3460aa77f6cSMauro Carvalho Chehab 
3470aa77f6cSMauro Carvalho Chehab static int debug;
3480aa77f6cSMauro Carvalho Chehab 
3490aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev);
3500aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev);
3515e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc);
3525e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc);
3535e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf,
3540aa77f6cSMauro Carvalho Chehab 			   int jpgsize);
3555e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode);
3560aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev);
3570aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset);
3580aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev);
3590aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
3600aa77f6cSMauro Carvalho Chehab 			     u16 index, u16 value, void *buf,
3610aa77f6cSMauro Carvalho Chehab 			     s32 buf_len, int bOut);
3620aa77f6cSMauro Carvalho Chehab 
3630aa77f6cSMauro Carvalho Chehab /* dev_err macro with driver name */
3640aa77f6cSMauro Carvalho Chehab #define S2255_DRIVER_NAME "s2255"
3650aa77f6cSMauro Carvalho Chehab #define s2255_dev_err(dev, fmt, arg...)					\
3660aa77f6cSMauro Carvalho Chehab 		dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
3670aa77f6cSMauro Carvalho Chehab 
368f5402007Ssensoray-dev #define dprintk(dev, level, fmt, arg...) \
369f5402007Ssensoray-dev 	v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
3700aa77f6cSMauro Carvalho Chehab 
3710aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver;
3720aa77f6cSMauro Carvalho Chehab 
3730aa77f6cSMauro Carvalho Chehab /* start video number */
3740aa77f6cSMauro Carvalho Chehab static int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
3750aa77f6cSMauro Carvalho Chehab 
3760aa77f6cSMauro Carvalho Chehab /* Enable jpeg capture. */
3770aa77f6cSMauro Carvalho Chehab static int jpeg_enable = 1;
3780aa77f6cSMauro Carvalho Chehab 
3790aa77f6cSMauro Carvalho Chehab module_param(debug, int, 0644);
3800aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
3810aa77f6cSMauro Carvalho Chehab module_param(video_nr, int, 0644);
3820aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
3830aa77f6cSMauro Carvalho Chehab module_param(jpeg_enable, int, 0644);
3840aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
3850aa77f6cSMauro Carvalho Chehab 
3860aa77f6cSMauro Carvalho Chehab /* USB device table */
3870aa77f6cSMauro Carvalho Chehab #define USB_SENSORAY_VID	0x1943
3880aa77f6cSMauro Carvalho Chehab static struct usb_device_id s2255_table[] = {
3890aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
3900aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
3910aa77f6cSMauro Carvalho Chehab 	{ }			/* Terminating entry */
3920aa77f6cSMauro Carvalho Chehab };
3930aa77f6cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, s2255_table);
3940aa77f6cSMauro Carvalho Chehab 
3950aa77f6cSMauro Carvalho Chehab #define BUFFER_TIMEOUT msecs_to_jiffies(400)
3960aa77f6cSMauro Carvalho Chehab 
3970aa77f6cSMauro Carvalho Chehab /* image formats.  */
3980aa77f6cSMauro Carvalho Chehab /* JPEG formats must be defined last to support jpeg_enable parameter */
3990aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt formats[] = {
4000aa77f6cSMauro Carvalho Chehab 	{
4010aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, packed, YUYV",
4020aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_YUYV,
4030aa77f6cSMauro Carvalho Chehab 		.depth = 16
4040aa77f6cSMauro Carvalho Chehab 
4050aa77f6cSMauro Carvalho Chehab 	}, {
4060aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, packed, UYVY",
4070aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_UYVY,
4080aa77f6cSMauro Carvalho Chehab 		.depth = 16
4090aa77f6cSMauro Carvalho Chehab 	}, {
4105c632b22SHans Verkuil 		.name = "4:2:2, planar, YUV422P",
4115c632b22SHans Verkuil 		.fourcc = V4L2_PIX_FMT_YUV422P,
4125c632b22SHans Verkuil 		.depth = 16
4135c632b22SHans Verkuil 
4145c632b22SHans Verkuil 	}, {
4150aa77f6cSMauro Carvalho Chehab 		.name = "8bpp GREY",
4160aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_GREY,
4170aa77f6cSMauro Carvalho Chehab 		.depth = 8
4180aa77f6cSMauro Carvalho Chehab 	}, {
4190aa77f6cSMauro Carvalho Chehab 		.name = "JPG",
4200aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_JPEG,
4210aa77f6cSMauro Carvalho Chehab 		.depth = 24
4220aa77f6cSMauro Carvalho Chehab 	}, {
4230aa77f6cSMauro Carvalho Chehab 		.name = "MJPG",
4240aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_MJPEG,
4250aa77f6cSMauro Carvalho Chehab 		.depth = 24
4260aa77f6cSMauro Carvalho Chehab 	}
4270aa77f6cSMauro Carvalho Chehab };
4280aa77f6cSMauro Carvalho Chehab 
4295e950fafSDean Anderson static int norm_maxw(struct s2255_vc *vc)
4300aa77f6cSMauro Carvalho Chehab {
4315e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4320aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
4330aa77f6cSMauro Carvalho Chehab }
4340aa77f6cSMauro Carvalho Chehab 
4355e950fafSDean Anderson static int norm_maxh(struct s2255_vc *vc)
4360aa77f6cSMauro Carvalho Chehab {
4375e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4380aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
4390aa77f6cSMauro Carvalho Chehab }
4400aa77f6cSMauro Carvalho Chehab 
4415e950fafSDean Anderson static int norm_minw(struct s2255_vc *vc)
4420aa77f6cSMauro Carvalho Chehab {
4435e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4440aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
4450aa77f6cSMauro Carvalho Chehab }
4460aa77f6cSMauro Carvalho Chehab 
4475e950fafSDean Anderson static int norm_minh(struct s2255_vc *vc)
4480aa77f6cSMauro Carvalho Chehab {
4495e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4500aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
4510aa77f6cSMauro Carvalho Chehab }
4520aa77f6cSMauro Carvalho Chehab 
4530aa77f6cSMauro Carvalho Chehab 
4540aa77f6cSMauro Carvalho Chehab /*
4550aa77f6cSMauro Carvalho Chehab  * TODO: fixme: move YUV reordering to hardware
4560aa77f6cSMauro Carvalho Chehab  * converts 2255 planar format to yuyv or uyvy
4570aa77f6cSMauro Carvalho Chehab  */
4580aa77f6cSMauro Carvalho Chehab static void planar422p_to_yuv_packed(const unsigned char *in,
4590aa77f6cSMauro Carvalho Chehab 				     unsigned char *out,
4600aa77f6cSMauro Carvalho Chehab 				     int width, int height,
4610aa77f6cSMauro Carvalho Chehab 				     int fmt)
4620aa77f6cSMauro Carvalho Chehab {
4630aa77f6cSMauro Carvalho Chehab 	unsigned char *pY;
4640aa77f6cSMauro Carvalho Chehab 	unsigned char *pCb;
4650aa77f6cSMauro Carvalho Chehab 	unsigned char *pCr;
4660aa77f6cSMauro Carvalho Chehab 	unsigned long size = height * width;
4670aa77f6cSMauro Carvalho Chehab 	unsigned int i;
4680aa77f6cSMauro Carvalho Chehab 	pY = (unsigned char *)in;
4690aa77f6cSMauro Carvalho Chehab 	pCr = (unsigned char *)in + height * width;
4700aa77f6cSMauro Carvalho Chehab 	pCb = (unsigned char *)in + height * width + (height * width / 2);
4710aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < size * 2; i += 4) {
4720aa77f6cSMauro Carvalho Chehab 		out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
4730aa77f6cSMauro Carvalho Chehab 		out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
4740aa77f6cSMauro Carvalho Chehab 		out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
4750aa77f6cSMauro Carvalho Chehab 		out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
4760aa77f6cSMauro Carvalho Chehab 	}
4770aa77f6cSMauro Carvalho Chehab 	return;
4780aa77f6cSMauro Carvalho Chehab }
4790aa77f6cSMauro Carvalho Chehab 
4800aa77f6cSMauro Carvalho Chehab static void s2255_reset_dsppower(struct s2255_dev *dev)
4810aa77f6cSMauro Carvalho Chehab {
4820aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
483f5402007Ssensoray-dev 	msleep(20);
4840aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
4850aa77f6cSMauro Carvalho Chehab 	msleep(600);
4860aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
4870aa77f6cSMauro Carvalho Chehab 	return;
4880aa77f6cSMauro Carvalho Chehab }
4890aa77f6cSMauro Carvalho Chehab 
4900aa77f6cSMauro Carvalho Chehab /* kickstarts the firmware loading. from probe
4910aa77f6cSMauro Carvalho Chehab  */
4920aa77f6cSMauro Carvalho Chehab static void s2255_timer(unsigned long user_data)
4930aa77f6cSMauro Carvalho Chehab {
4940aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = (struct s2255_fw *)user_data;
4950aa77f6cSMauro Carvalho Chehab 	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
496f5402007Ssensoray-dev 		pr_err("s2255: can't submit urb\n");
4970aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
4980aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
4990aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5000aa77f6cSMauro Carvalho Chehab 		return;
5010aa77f6cSMauro Carvalho Chehab 	}
5020aa77f6cSMauro Carvalho Chehab }
5030aa77f6cSMauro Carvalho Chehab 
5040aa77f6cSMauro Carvalho Chehab 
5050aa77f6cSMauro Carvalho Chehab /* this loads the firmware asynchronously.
5060b84caabSHans Verkuil    Originally this was done synchronously in probe.
5070aa77f6cSMauro Carvalho Chehab    But it is better to load it asynchronously here than block
5080aa77f6cSMauro Carvalho Chehab    inside the probe function. Blocking inside probe affects boot time.
5090aa77f6cSMauro Carvalho Chehab    FW loading is triggered by the timer in the probe function
5100aa77f6cSMauro Carvalho Chehab */
5110aa77f6cSMauro Carvalho Chehab static void s2255_fwchunk_complete(struct urb *urb)
5120aa77f6cSMauro Carvalho Chehab {
5130aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = urb->context;
5140aa77f6cSMauro Carvalho Chehab 	struct usb_device *udev = urb->dev;
5150aa77f6cSMauro Carvalho Chehab 	int len;
5160aa77f6cSMauro Carvalho Chehab 	if (urb->status) {
5170aa77f6cSMauro Carvalho Chehab 		dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
5180aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5190aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5200aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5210aa77f6cSMauro Carvalho Chehab 		return;
5220aa77f6cSMauro Carvalho Chehab 	}
5230aa77f6cSMauro Carvalho Chehab 	if (data->fw_urb == NULL) {
5240aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&udev->dev, "disconnected\n");
5250aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5260aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5270aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5280aa77f6cSMauro Carvalho Chehab 		return;
5290aa77f6cSMauro Carvalho Chehab 	}
5300aa77f6cSMauro Carvalho Chehab #define CHUNK_SIZE 512
5310aa77f6cSMauro Carvalho Chehab 	/* all USB transfers must be done with continuous kernel memory.
5320aa77f6cSMauro Carvalho Chehab 	   can't allocate more than 128k in current linux kernel, so
5330aa77f6cSMauro Carvalho Chehab 	   upload the firmware in chunks
5340aa77f6cSMauro Carvalho Chehab 	 */
5350aa77f6cSMauro Carvalho Chehab 	if (data->fw_loaded < data->fw_size) {
5360aa77f6cSMauro Carvalho Chehab 		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
5370aa77f6cSMauro Carvalho Chehab 		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
5380aa77f6cSMauro Carvalho Chehab 
5390aa77f6cSMauro Carvalho Chehab 		if (len < CHUNK_SIZE)
5400aa77f6cSMauro Carvalho Chehab 			memset(data->pfw_data, 0, CHUNK_SIZE);
5410aa77f6cSMauro Carvalho Chehab 
5420aa77f6cSMauro Carvalho Chehab 		memcpy(data->pfw_data,
5430aa77f6cSMauro Carvalho Chehab 		       (char *) data->fw->data + data->fw_loaded, len);
5440aa77f6cSMauro Carvalho Chehab 
5450aa77f6cSMauro Carvalho Chehab 		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
5460aa77f6cSMauro Carvalho Chehab 				  data->pfw_data, CHUNK_SIZE,
5470aa77f6cSMauro Carvalho Chehab 				  s2255_fwchunk_complete, data);
5480aa77f6cSMauro Carvalho Chehab 		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
5490aa77f6cSMauro Carvalho Chehab 			dev_err(&udev->dev, "failed submit URB\n");
5500aa77f6cSMauro Carvalho Chehab 			atomic_set(&data->fw_state, S2255_FW_FAILED);
5510aa77f6cSMauro Carvalho Chehab 			/* wake up anything waiting for the firmware */
5520aa77f6cSMauro Carvalho Chehab 			wake_up(&data->wait_fw);
5530aa77f6cSMauro Carvalho Chehab 			return;
5540aa77f6cSMauro Carvalho Chehab 		}
5550aa77f6cSMauro Carvalho Chehab 		data->fw_loaded += len;
556f5402007Ssensoray-dev 	} else
5570aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
5580aa77f6cSMauro Carvalho Chehab 	return;
5590aa77f6cSMauro Carvalho Chehab 
5600aa77f6cSMauro Carvalho Chehab }
5610aa77f6cSMauro Carvalho Chehab 
5629694fbecSsensoray-dev static void s2255_got_frame(struct s2255_vc *vc, int jpgsize)
5630aa77f6cSMauro Carvalho Chehab {
5640aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf;
5655e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
5660aa77f6cSMauro Carvalho Chehab 	unsigned long flags = 0;
5679694fbecSsensoray-dev 
568340a30c5Ssensoray-dev 	spin_lock_irqsave(&vc->qlock, flags);
5695e950fafSDean Anderson 	if (list_empty(&vc->buf_list)) {
570f5402007Ssensoray-dev 		dprintk(dev, 1, "No active queue to serve\n");
5719694fbecSsensoray-dev 		spin_unlock_irqrestore(&vc->qlock, flags);
5729694fbecSsensoray-dev 		return;
5730aa77f6cSMauro Carvalho Chehab 	}
5745e950fafSDean Anderson 	buf = list_entry(vc->buf_list.next,
575340a30c5Ssensoray-dev 			 struct s2255_buffer, list);
576340a30c5Ssensoray-dev 	list_del(&buf->list);
577d6dd645eSJunghak Sung 	buf->vb.vb2_buf.timestamp = ktime_get_ns();
5782d700715SJunghak Sung 	buf->vb.field = vc->field;
5792d700715SJunghak Sung 	buf->vb.sequence = vc->frame_count;
580340a30c5Ssensoray-dev 	spin_unlock_irqrestore(&vc->qlock, flags);
5819694fbecSsensoray-dev 
5829694fbecSsensoray-dev 	s2255_fillbuff(vc, buf, jpgsize);
5839694fbecSsensoray-dev 	/* tell v4l buffer was filled */
5842d700715SJunghak Sung 	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
5859694fbecSsensoray-dev 	dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
5860aa77f6cSMauro Carvalho Chehab }
5870aa77f6cSMauro Carvalho Chehab 
5880aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt *format_by_fourcc(int fourcc)
5890aa77f6cSMauro Carvalho Chehab {
5900aa77f6cSMauro Carvalho Chehab 	unsigned int i;
5910aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
5920aa77f6cSMauro Carvalho Chehab 		if (-1 == formats[i].fourcc)
5930aa77f6cSMauro Carvalho Chehab 			continue;
5940aa77f6cSMauro Carvalho Chehab 		if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
5950aa77f6cSMauro Carvalho Chehab 				     (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
5960aa77f6cSMauro Carvalho Chehab 			continue;
5970aa77f6cSMauro Carvalho Chehab 		if (formats[i].fourcc == fourcc)
5980aa77f6cSMauro Carvalho Chehab 			return formats + i;
5990aa77f6cSMauro Carvalho Chehab 	}
6000aa77f6cSMauro Carvalho Chehab 	return NULL;
6010aa77f6cSMauro Carvalho Chehab }
6020aa77f6cSMauro Carvalho Chehab 
6030aa77f6cSMauro Carvalho Chehab /* video buffer vmalloc implementation based partly on VIVI driver which is
6040aa77f6cSMauro Carvalho Chehab  *          Copyright (c) 2006 by
6050aa77f6cSMauro Carvalho Chehab  *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
6060aa77f6cSMauro Carvalho Chehab  *                  Ted Walther <ted--a.t--enumera.com>
6070aa77f6cSMauro Carvalho Chehab  *                  John Sokol <sokol--a.t--videotechnology.com>
6080aa77f6cSMauro Carvalho Chehab  *                  http://v4l.videotechnology.com/
6090aa77f6cSMauro Carvalho Chehab  *
6100aa77f6cSMauro Carvalho Chehab  */
6115e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc,
6120aa77f6cSMauro Carvalho Chehab 			   struct s2255_buffer *buf, int jpgsize)
6130aa77f6cSMauro Carvalho Chehab {
6140aa77f6cSMauro Carvalho Chehab 	int pos = 0;
6150aa77f6cSMauro Carvalho Chehab 	const char *tmpbuf;
6162d700715SJunghak Sung 	char *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
6170aa77f6cSMauro Carvalho Chehab 	unsigned long last_frame;
6185e950fafSDean Anderson 	struct s2255_dev *dev = vc->dev;
6190aa77f6cSMauro Carvalho Chehab 
6200aa77f6cSMauro Carvalho Chehab 	if (!vbuf)
6210aa77f6cSMauro Carvalho Chehab 		return;
6225e950fafSDean Anderson 	last_frame = vc->last_frame;
6230aa77f6cSMauro Carvalho Chehab 	if (last_frame != -1) {
6240aa77f6cSMauro Carvalho Chehab 		tmpbuf =
6255e950fafSDean Anderson 		    (const char *)vc->buffer.frame[last_frame].lpvbits;
6268bf405a0SDean Anderson 		switch (vc->fmt->fourcc) {
6270aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUYV:
6280aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_UYVY:
6290aa77f6cSMauro Carvalho Chehab 			planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
630340a30c5Ssensoray-dev 						 vbuf, vc->width,
631340a30c5Ssensoray-dev 						 vc->height,
6328bf405a0SDean Anderson 						 vc->fmt->fourcc);
6330aa77f6cSMauro Carvalho Chehab 			break;
6340aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_GREY:
635340a30c5Ssensoray-dev 			memcpy(vbuf, tmpbuf, vc->width * vc->height);
6360aa77f6cSMauro Carvalho Chehab 			break;
6370aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_JPEG:
6380aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_MJPEG:
6392d700715SJunghak Sung 			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
640340a30c5Ssensoray-dev 			memcpy(vbuf, tmpbuf, jpgsize);
6410aa77f6cSMauro Carvalho Chehab 			break;
6420aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUV422P:
6430aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf,
644340a30c5Ssensoray-dev 			       vc->width * vc->height * 2);
6450aa77f6cSMauro Carvalho Chehab 			break;
6460aa77f6cSMauro Carvalho Chehab 		default:
647f5402007Ssensoray-dev 			pr_info("s2255: unknown format?\n");
6480aa77f6cSMauro Carvalho Chehab 		}
6495e950fafSDean Anderson 		vc->last_frame = -1;
6500aa77f6cSMauro Carvalho Chehab 	} else {
651f5402007Ssensoray-dev 		pr_err("s2255: =======no frame\n");
6520aa77f6cSMauro Carvalho Chehab 		return;
6530aa77f6cSMauro Carvalho Chehab 	}
654f5402007Ssensoray-dev 	dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n",
6550aa77f6cSMauro Carvalho Chehab 		(unsigned long)vbuf, pos);
6560aa77f6cSMauro Carvalho Chehab }
6570aa77f6cSMauro Carvalho Chehab 
6580aa77f6cSMauro Carvalho Chehab 
6590aa77f6cSMauro Carvalho Chehab /* ------------------------------------------------------------------
6600aa77f6cSMauro Carvalho Chehab    Videobuf operations
6610aa77f6cSMauro Carvalho Chehab    ------------------------------------------------------------------*/
6620aa77f6cSMauro Carvalho Chehab 
663df9ecb0cSHans Verkuil static int queue_setup(struct vb2_queue *vq,
664340a30c5Ssensoray-dev 		       unsigned int *nbuffers, unsigned int *nplanes,
66536c0f8b3SHans Verkuil 		       unsigned int sizes[], struct device *alloc_devs[])
6660aa77f6cSMauro Carvalho Chehab {
667340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vq);
6689da62eb0SDean Anderson 	if (*nbuffers < S2255_MIN_BUFS)
6699da62eb0SDean Anderson 		*nbuffers = S2255_MIN_BUFS;
670340a30c5Ssensoray-dev 	*nplanes = 1;
671340a30c5Ssensoray-dev 	sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3);
6720aa77f6cSMauro Carvalho Chehab 	return 0;
6730aa77f6cSMauro Carvalho Chehab }
6740aa77f6cSMauro Carvalho Chehab 
675340a30c5Ssensoray-dev static int buffer_prepare(struct vb2_buffer *vb)
6760aa77f6cSMauro Carvalho Chehab {
677340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
6782d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6792d700715SJunghak Sung 	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
6805e950fafSDean Anderson 	int w = vc->width;
6815e950fafSDean Anderson 	int h = vc->height;
682340a30c5Ssensoray-dev 	unsigned long size;
683340a30c5Ssensoray-dev 
684340a30c5Ssensoray-dev 	dprintk(vc->dev, 4, "%s\n", __func__);
6855e950fafSDean Anderson 	if (vc->fmt == NULL)
6860aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
6870aa77f6cSMauro Carvalho Chehab 
6885e950fafSDean Anderson 	if ((w < norm_minw(vc)) ||
6895e950fafSDean Anderson 	    (w > norm_maxw(vc)) ||
6905e950fafSDean Anderson 	    (h < norm_minh(vc)) ||
6915e950fafSDean Anderson 	    (h > norm_maxh(vc))) {
69292cde477SDean Anderson 		dprintk(vc->dev, 4, "invalid buffer prepare\n");
6930aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
6940aa77f6cSMauro Carvalho Chehab 	}
695340a30c5Ssensoray-dev 	size = w * h * (vc->fmt->depth >> 3);
696340a30c5Ssensoray-dev 	if (vb2_plane_size(vb, 0) < size) {
69792cde477SDean Anderson 		dprintk(vc->dev, 4, "invalid buffer prepare\n");
6980aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
6990aa77f6cSMauro Carvalho Chehab 	}
7000aa77f6cSMauro Carvalho Chehab 
7012d700715SJunghak Sung 	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
7020aa77f6cSMauro Carvalho Chehab 	return 0;
7030aa77f6cSMauro Carvalho Chehab }
7040aa77f6cSMauro Carvalho Chehab 
705340a30c5Ssensoray-dev static void buffer_queue(struct vb2_buffer *vb)
7060aa77f6cSMauro Carvalho Chehab {
7072d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
7082d700715SJunghak Sung 	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
709340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
710340a30c5Ssensoray-dev 	unsigned long flags = 0;
71192cde477SDean Anderson 	dprintk(vc->dev, 1, "%s\n", __func__);
712340a30c5Ssensoray-dev 	spin_lock_irqsave(&vc->qlock, flags);
713340a30c5Ssensoray-dev 	list_add_tail(&buf->list, &vc->buf_list);
714340a30c5Ssensoray-dev 	spin_unlock_irqrestore(&vc->qlock, flags);
7150aa77f6cSMauro Carvalho Chehab }
7160aa77f6cSMauro Carvalho Chehab 
717340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count);
718e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq);
7190aa77f6cSMauro Carvalho Chehab 
7201bc17717SJulia Lawall static const struct vb2_ops s2255_video_qops = {
721340a30c5Ssensoray-dev 	.queue_setup = queue_setup,
7220aa77f6cSMauro Carvalho Chehab 	.buf_prepare = buffer_prepare,
7230aa77f6cSMauro Carvalho Chehab 	.buf_queue = buffer_queue,
724340a30c5Ssensoray-dev 	.start_streaming = start_streaming,
725340a30c5Ssensoray-dev 	.stop_streaming = stop_streaming,
726340a30c5Ssensoray-dev 	.wait_prepare = vb2_ops_wait_prepare,
727340a30c5Ssensoray-dev 	.wait_finish = vb2_ops_wait_finish,
7280aa77f6cSMauro Carvalho Chehab };
7290aa77f6cSMauro Carvalho Chehab 
7300aa77f6cSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv,
7310aa77f6cSMauro Carvalho Chehab 			   struct v4l2_capability *cap)
7320aa77f6cSMauro Carvalho Chehab {
733340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
734340a30c5Ssensoray-dev 	struct s2255_dev *dev = vc->dev;
73539696009SHans Verkuil 
7360aa77f6cSMauro Carvalho Chehab 	strlcpy(cap->driver, "s2255", sizeof(cap->driver));
7370aa77f6cSMauro Carvalho Chehab 	strlcpy(cap->card, "s2255", sizeof(cap->card));
7380aa77f6cSMauro Carvalho Chehab 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
739340a30c5Ssensoray-dev 	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
740340a30c5Ssensoray-dev 		V4L2_CAP_READWRITE;
74139696009SHans Verkuil 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
7420aa77f6cSMauro Carvalho Chehab 	return 0;
7430aa77f6cSMauro Carvalho Chehab }
7440aa77f6cSMauro Carvalho Chehab 
7450aa77f6cSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
7460aa77f6cSMauro Carvalho Chehab 			       struct v4l2_fmtdesc *f)
7470aa77f6cSMauro Carvalho Chehab {
7480aa77f6cSMauro Carvalho Chehab 	int index = f->index;
7490aa77f6cSMauro Carvalho Chehab 
7500aa77f6cSMauro Carvalho Chehab 	if (index >= ARRAY_SIZE(formats))
7510aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7520aa77f6cSMauro Carvalho Chehab 	if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
7530aa77f6cSMauro Carvalho Chehab 			(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
7540aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7550aa77f6cSMauro Carvalho Chehab 	strlcpy(f->description, formats[index].name, sizeof(f->description));
7560aa77f6cSMauro Carvalho Chehab 	f->pixelformat = formats[index].fourcc;
7570aa77f6cSMauro Carvalho Chehab 	return 0;
7580aa77f6cSMauro Carvalho Chehab }
7590aa77f6cSMauro Carvalho Chehab 
7600aa77f6cSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
7610aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
7620aa77f6cSMauro Carvalho Chehab {
763340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
7645e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
7650aa77f6cSMauro Carvalho Chehab 
7665e950fafSDean Anderson 	f->fmt.pix.width = vc->width;
7675e950fafSDean Anderson 	f->fmt.pix.height = vc->height;
76892513611SHans Verkuil 	if (f->fmt.pix.height >=
76992513611SHans Verkuil 	    (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2)
77092513611SHans Verkuil 		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
77192513611SHans Verkuil 	else
77292513611SHans Verkuil 		f->fmt.pix.field = V4L2_FIELD_TOP;
7735e950fafSDean Anderson 	f->fmt.pix.pixelformat = vc->fmt->fourcc;
7745e950fafSDean Anderson 	f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3);
7750aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
77629ceb110SHans Verkuil 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
77729ceb110SHans Verkuil 	f->fmt.pix.priv = 0;
7780aa77f6cSMauro Carvalho Chehab 	return 0;
7790aa77f6cSMauro Carvalho Chehab }
7800aa77f6cSMauro Carvalho Chehab 
7810aa77f6cSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
7820aa77f6cSMauro Carvalho Chehab 			      struct v4l2_format *f)
7830aa77f6cSMauro Carvalho Chehab {
7840aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
7850aa77f6cSMauro Carvalho Chehab 	enum v4l2_field field;
786340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
7875e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
7880aa77f6cSMauro Carvalho Chehab 
7890aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
7900aa77f6cSMauro Carvalho Chehab 
7910aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
7920aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7930aa77f6cSMauro Carvalho Chehab 
7940aa77f6cSMauro Carvalho Chehab 	field = f->fmt.pix.field;
7950aa77f6cSMauro Carvalho Chehab 
79692cde477SDean Anderson 	dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n",
7970aa77f6cSMauro Carvalho Chehab 		__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
7980aa77f6cSMauro Carvalho Chehab 	if (is_ntsc) {
7990aa77f6cSMauro Carvalho Chehab 		/* NTSC */
8000aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
8010aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
80292513611SHans Verkuil 			field = V4L2_FIELD_INTERLACED;
8030aa77f6cSMauro Carvalho Chehab 		} else {
8040aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
8050aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
8060aa77f6cSMauro Carvalho Chehab 		}
8070aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
8080aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
8090aa77f6cSMauro Carvalho Chehab 		else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
8100aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
8110aa77f6cSMauro Carvalho Chehab 		else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
8120aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
8130aa77f6cSMauro Carvalho Chehab 		else
8140aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
8150aa77f6cSMauro Carvalho Chehab 	} else {
8160aa77f6cSMauro Carvalho Chehab 		/* PAL */
8170aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
8180aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
81992513611SHans Verkuil 			field = V4L2_FIELD_INTERLACED;
8200aa77f6cSMauro Carvalho Chehab 		} else {
8210aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
8220aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
8230aa77f6cSMauro Carvalho Chehab 		}
82492513611SHans Verkuil 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL)
8250aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
82692513611SHans Verkuil 		else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL)
8270aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
82892513611SHans Verkuil 		else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL)
8290aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
83092513611SHans Verkuil 		else
8310aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
8320aa77f6cSMauro Carvalho Chehab 	}
8330aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.field = field;
8340aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
8350aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
83629ceb110SHans Verkuil 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
83729ceb110SHans Verkuil 	f->fmt.pix.priv = 0;
83892cde477SDean Anderson 	dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__,
8390aa77f6cSMauro Carvalho Chehab 		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
8400aa77f6cSMauro Carvalho Chehab 	return 0;
8410aa77f6cSMauro Carvalho Chehab }
8420aa77f6cSMauro Carvalho Chehab 
8430aa77f6cSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
8440aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
8450aa77f6cSMauro Carvalho Chehab {
846340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
8470aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
848340a30c5Ssensoray-dev 	struct vb2_queue *q = &vc->vb_vidq;
8490aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
8500aa77f6cSMauro Carvalho Chehab 	int ret;
8510aa77f6cSMauro Carvalho Chehab 
852340a30c5Ssensoray-dev 	ret = vidioc_try_fmt_vid_cap(file, vc, f);
8530aa77f6cSMauro Carvalho Chehab 
8540aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
8550aa77f6cSMauro Carvalho Chehab 		return ret;
8560aa77f6cSMauro Carvalho Chehab 
8570aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
8580aa77f6cSMauro Carvalho Chehab 
8590aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
8600aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8610aa77f6cSMauro Carvalho Chehab 
862340a30c5Ssensoray-dev 	if (vb2_is_busy(q)) {
86392cde477SDean Anderson 		dprintk(vc->dev, 1, "queue busy\n");
864340a30c5Ssensoray-dev 		return -EBUSY;
8650aa77f6cSMauro Carvalho Chehab 	}
8660aa77f6cSMauro Carvalho Chehab 
8675e950fafSDean Anderson 	mode = vc->mode;
8685e950fafSDean Anderson 	vc->fmt = fmt;
8695e950fafSDean Anderson 	vc->width = f->fmt.pix.width;
8705e950fafSDean Anderson 	vc->height = f->fmt.pix.height;
871340a30c5Ssensoray-dev 	vc->field = f->fmt.pix.field;
8725e950fafSDean Anderson 	if (vc->width > norm_minw(vc)) {
8735e950fafSDean Anderson 		if (vc->height > norm_minh(vc)) {
8745e950fafSDean Anderson 			if (vc->cap_parm.capturemode &
8750aa77f6cSMauro Carvalho Chehab 			    V4L2_MODE_HIGHQUALITY)
8760aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFSI;
8770aa77f6cSMauro Carvalho Chehab 			else
8780aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFS;
8790aa77f6cSMauro Carvalho Chehab 		} else
8800aa77f6cSMauro Carvalho Chehab 			mode.scale = SCALE_2CIFS;
8810aa77f6cSMauro Carvalho Chehab 
8820aa77f6cSMauro Carvalho Chehab 	} else {
8830aa77f6cSMauro Carvalho Chehab 		mode.scale = SCALE_1CIFS;
8840aa77f6cSMauro Carvalho Chehab 	}
8850aa77f6cSMauro Carvalho Chehab 	/* color mode */
8865e950fafSDean Anderson 	switch (vc->fmt->fourcc) {
8870aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_GREY:
8880aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8890aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_Y8;
8900aa77f6cSMauro Carvalho Chehab 		break;
8910aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_JPEG:
8920aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_MJPEG:
8930aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8940aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_JPG;
8955e950fafSDean Anderson 		mode.color |= (vc->jpegqual << 8);
8960aa77f6cSMauro Carvalho Chehab 		break;
8970aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUV422P:
8980aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8990aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPL;
9000aa77f6cSMauro Carvalho Chehab 		break;
9010aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUYV:
9020aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_UYVY:
9030aa77f6cSMauro Carvalho Chehab 	default:
9040aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
9050aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPK;
9060aa77f6cSMauro Carvalho Chehab 		break;
9070aa77f6cSMauro Carvalho Chehab 	}
9085e950fafSDean Anderson 	if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR))
9090aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
9105e950fafSDean Anderson 	else if (mode.scale != vc->mode.scale)
9110aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
9125e950fafSDean Anderson 	else if (mode.format != vc->mode.format)
9130aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
9145e950fafSDean Anderson 	vc->mode = mode;
9155e950fafSDean Anderson 	(void) s2255_set_mode(vc, &mode);
916340a30c5Ssensoray-dev 	return 0;
9170aa77f6cSMauro Carvalho Chehab }
9180aa77f6cSMauro Carvalho Chehab 
9190aa77f6cSMauro Carvalho Chehab 
9200aa77f6cSMauro Carvalho Chehab /* write to the configuration pipe, synchronously */
9210aa77f6cSMauro Carvalho Chehab static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
9220aa77f6cSMauro Carvalho Chehab 			      int size)
9230aa77f6cSMauro Carvalho Chehab {
9240aa77f6cSMauro Carvalho Chehab 	int pipe;
9250aa77f6cSMauro Carvalho Chehab 	int done;
9260aa77f6cSMauro Carvalho Chehab 	long retval = -1;
9270aa77f6cSMauro Carvalho Chehab 	if (udev) {
9280aa77f6cSMauro Carvalho Chehab 		pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
9290aa77f6cSMauro Carvalho Chehab 		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
9300aa77f6cSMauro Carvalho Chehab 	}
9310aa77f6cSMauro Carvalho Chehab 	return retval;
9320aa77f6cSMauro Carvalho Chehab }
9330aa77f6cSMauro Carvalho Chehab 
9340aa77f6cSMauro Carvalho Chehab static u32 get_transfer_size(struct s2255_mode *mode)
9350aa77f6cSMauro Carvalho Chehab {
9360aa77f6cSMauro Carvalho Chehab 	int linesPerFrame = LINE_SZ_DEF;
9370aa77f6cSMauro Carvalho Chehab 	int pixelsPerLine = NUM_LINES_DEF;
9380aa77f6cSMauro Carvalho Chehab 	u32 outImageSize;
9390aa77f6cSMauro Carvalho Chehab 	u32 usbInSize;
9400aa77f6cSMauro Carvalho Chehab 	unsigned int mask_mult;
9410aa77f6cSMauro Carvalho Chehab 
9420aa77f6cSMauro Carvalho Chehab 	if (mode == NULL)
9430aa77f6cSMauro Carvalho Chehab 		return 0;
9440aa77f6cSMauro Carvalho Chehab 
9450aa77f6cSMauro Carvalho Chehab 	if (mode->format == FORMAT_NTSC) {
9460aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
9470aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
9480aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
9490aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
9500aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
9510aa77f6cSMauro Carvalho Chehab 			break;
9520aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
9530aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_NTSC;
9540aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
9550aa77f6cSMauro Carvalho Chehab 			break;
9560aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
9570aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_NTSC;
9580aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
9590aa77f6cSMauro Carvalho Chehab 			break;
9600aa77f6cSMauro Carvalho Chehab 		default:
9610aa77f6cSMauro Carvalho Chehab 			break;
9620aa77f6cSMauro Carvalho Chehab 		}
9630aa77f6cSMauro Carvalho Chehab 	} else if (mode->format == FORMAT_PAL) {
9640aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
9650aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
9660aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
9670aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
9680aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_PAL;
9690aa77f6cSMauro Carvalho Chehab 			break;
9700aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
9710aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_PAL;
9720aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_PAL;
9730aa77f6cSMauro Carvalho Chehab 			break;
9740aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
9750aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_PAL;
9760aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_PAL;
9770aa77f6cSMauro Carvalho Chehab 			break;
9780aa77f6cSMauro Carvalho Chehab 		default:
9790aa77f6cSMauro Carvalho Chehab 			break;
9800aa77f6cSMauro Carvalho Chehab 		}
9810aa77f6cSMauro Carvalho Chehab 	}
9820aa77f6cSMauro Carvalho Chehab 	outImageSize = linesPerFrame * pixelsPerLine;
9830aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) != COLOR_Y8) {
9840aa77f6cSMauro Carvalho Chehab 		/* 2 bytes/pixel if not monochrome */
9850aa77f6cSMauro Carvalho Chehab 		outImageSize *= 2;
9860aa77f6cSMauro Carvalho Chehab 	}
9870aa77f6cSMauro Carvalho Chehab 
9880aa77f6cSMauro Carvalho Chehab 	/* total bytes to send including prefix and 4K padding;
9890aa77f6cSMauro Carvalho Chehab 	   must be a multiple of USB_READ_SIZE */
9900aa77f6cSMauro Carvalho Chehab 	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
9910aa77f6cSMauro Carvalho Chehab 	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
9920aa77f6cSMauro Carvalho Chehab 	/* if size not a multiple of USB_READ_SIZE */
9930aa77f6cSMauro Carvalho Chehab 	if (usbInSize & ~mask_mult)
9940aa77f6cSMauro Carvalho Chehab 		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
9950aa77f6cSMauro Carvalho Chehab 	return usbInSize;
9960aa77f6cSMauro Carvalho Chehab }
9970aa77f6cSMauro Carvalho Chehab 
9980aa77f6cSMauro Carvalho Chehab static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
9990aa77f6cSMauro Carvalho Chehab {
10000aa77f6cSMauro Carvalho Chehab 	struct device *dev = &sdev->udev->dev;
10010aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
10020aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
10030aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
10040aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "bright: 0x%x\n", mode->bright);
10050aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
10060aa77f6cSMauro Carvalho Chehab }
10070aa77f6cSMauro Carvalho Chehab 
10080aa77f6cSMauro Carvalho Chehab /*
10090aa77f6cSMauro Carvalho Chehab  * set mode is the function which controls the DSP.
10100aa77f6cSMauro Carvalho Chehab  * the restart parameter in struct s2255_mode should be set whenever
10110aa77f6cSMauro Carvalho Chehab  * the image size could change via color format, video system or image
10120aa77f6cSMauro Carvalho Chehab  * size.
10130aa77f6cSMauro Carvalho Chehab  * When the restart parameter is set, we sleep for ONE frame to allow the
10140aa77f6cSMauro Carvalho Chehab  * DSP time to get the new frame
10150aa77f6cSMauro Carvalho Chehab  */
10165e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc,
10170aa77f6cSMauro Carvalho Chehab 			  struct s2255_mode *mode)
10180aa77f6cSMauro Carvalho Chehab {
10190aa77f6cSMauro Carvalho Chehab 	int res;
10200aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
10215e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
10220b84caabSHans Verkuil 	int i;
102347d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
10240b84caabSHans Verkuil 
102547d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
10265e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
10275e950fafSDean Anderson 	dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx);
10280aa77f6cSMauro Carvalho Chehab 	/* if JPEG, set the quality */
10290aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) == COLOR_JPG) {
10300aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_COLOR;
10310aa77f6cSMauro Carvalho Chehab 		mode->color |= COLOR_JPG;
10320aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_JPG_QUALITY;
10335e950fafSDean Anderson 		mode->color |= (vc->jpegqual << 8);
10340aa77f6cSMauro Carvalho Chehab 	}
10350aa77f6cSMauro Carvalho Chehab 	/* save the mode */
10365e950fafSDean Anderson 	vc->mode = *mode;
10375e950fafSDean Anderson 	vc->req_image_size = get_transfer_size(mode);
10385e950fafSDean Anderson 	dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size);
10390aa77f6cSMauro Carvalho Chehab 	/* set the mode */
10400aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
10410aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
10420aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_SET_MODE;
10430b84caabSHans Verkuil 	for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++)
10445e950fafSDean Anderson 		buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]);
10455e950fafSDean Anderson 	vc->setmode_ready = 0;
10460aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
10470aa77f6cSMauro Carvalho Chehab 	if (debug)
10480aa77f6cSMauro Carvalho Chehab 		s2255_print_cfg(dev, mode);
10490aa77f6cSMauro Carvalho Chehab 	/* wait at least 3 frames before continuing */
10500aa77f6cSMauro Carvalho Chehab 	if (mode->restart) {
10515e950fafSDean Anderson 		wait_event_timeout(vc->wait_setmode,
10525e950fafSDean Anderson 				   (vc->setmode_ready != 0),
10530aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
10545e950fafSDean Anderson 		if (vc->setmode_ready != 1) {
1055f5402007Ssensoray-dev 			dprintk(dev, 0, "s2255: no set mode response\n");
10560aa77f6cSMauro Carvalho Chehab 			res = -EFAULT;
10570aa77f6cSMauro Carvalho Chehab 		}
10580aa77f6cSMauro Carvalho Chehab 	}
10590aa77f6cSMauro Carvalho Chehab 	/* clear the restart flag */
10605e950fafSDean Anderson 	vc->mode.restart = 0;
10615e950fafSDean Anderson 	dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res);
106247d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
10630aa77f6cSMauro Carvalho Chehab 	return res;
10640aa77f6cSMauro Carvalho Chehab }
10650aa77f6cSMauro Carvalho Chehab 
10665e950fafSDean Anderson static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus)
10670aa77f6cSMauro Carvalho Chehab {
10680aa77f6cSMauro Carvalho Chehab 	int res;
10690aa77f6cSMauro Carvalho Chehab 	u32 chn_rev;
10705e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
107147d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
107247d8c881SDean Anderson 
107347d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
10745e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
10755e950fafSDean Anderson 	dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx);
10760aa77f6cSMauro Carvalho Chehab 	/* form the get vid status command */
10770aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
10780aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
10790aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_STATUS;
10800aa77f6cSMauro Carvalho Chehab 	*pstatus = 0;
10815e950fafSDean Anderson 	vc->vidstatus_ready = 0;
10820aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
10835e950fafSDean Anderson 	wait_event_timeout(vc->wait_vidstatus,
10845e950fafSDean Anderson 			   (vc->vidstatus_ready != 0),
10850aa77f6cSMauro Carvalho Chehab 			   msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
10865e950fafSDean Anderson 	if (vc->vidstatus_ready != 1) {
1087f5402007Ssensoray-dev 		dprintk(dev, 0, "s2255: no vidstatus response\n");
10880aa77f6cSMauro Carvalho Chehab 		res = -EFAULT;
10890aa77f6cSMauro Carvalho Chehab 	}
10905e950fafSDean Anderson 	*pstatus = vc->vidstatus;
1091f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus);
109247d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
10930aa77f6cSMauro Carvalho Chehab 	return res;
10940aa77f6cSMauro Carvalho Chehab }
10950aa77f6cSMauro Carvalho Chehab 
1096340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count)
10970aa77f6cSMauro Carvalho Chehab {
1098340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vq);
10990aa77f6cSMauro Carvalho Chehab 	int j;
110092cde477SDean Anderson 
11015e950fafSDean Anderson 	vc->last_frame = -1;
11025e950fafSDean Anderson 	vc->bad_payload = 0;
11035e950fafSDean Anderson 	vc->cur_frame = 0;
11045e950fafSDean Anderson 	vc->frame_count = 0;
11050aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
11065e950fafSDean Anderson 		vc->buffer.frame[j].ulState = S2255_READ_IDLE;
11075e950fafSDean Anderson 		vc->buffer.frame[j].cur_size = 0;
11080aa77f6cSMauro Carvalho Chehab 	}
1109340a30c5Ssensoray-dev 	return s2255_start_acquire(vc);
11100aa77f6cSMauro Carvalho Chehab }
11110aa77f6cSMauro Carvalho Chehab 
1112340a30c5Ssensoray-dev /* abort streaming and wait for last buffer */
1113e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq)
11140aa77f6cSMauro Carvalho Chehab {
1115340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vq);
1116340a30c5Ssensoray-dev 	struct s2255_buffer *buf, *node;
1117340a30c5Ssensoray-dev 	unsigned long flags;
1118340a30c5Ssensoray-dev 	(void) s2255_stop_acquire(vc);
1119340a30c5Ssensoray-dev 	spin_lock_irqsave(&vc->qlock, flags);
1120340a30c5Ssensoray-dev 	list_for_each_entry_safe(buf, node, &vc->buf_list, list) {
1121340a30c5Ssensoray-dev 		list_del(&buf->list);
11222d700715SJunghak Sung 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
1123340a30c5Ssensoray-dev 		dprintk(vc->dev, 2, "[%p/%d] done\n",
11242d700715SJunghak Sung 			buf, buf->vb.vb2_buf.index);
1125340a30c5Ssensoray-dev 	}
1126340a30c5Ssensoray-dev 	spin_unlock_irqrestore(&vc->qlock, flags);
11270aa77f6cSMauro Carvalho Chehab }
11280aa77f6cSMauro Carvalho Chehab 
1129314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
11300aa77f6cSMauro Carvalho Chehab {
1131340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
11320aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
1133340a30c5Ssensoray-dev 	struct vb2_queue *q = &vc->vb_vidq;
1134469af77aSHans Verkuil 
1135340a30c5Ssensoray-dev 	/*
1136340a30c5Ssensoray-dev 	 * Changing the standard implies a format change, which is not allowed
1137340a30c5Ssensoray-dev 	 * while buffers for use with streaming have already been allocated.
1138340a30c5Ssensoray-dev 	 */
1139340a30c5Ssensoray-dev 	if (vb2_is_busy(q))
1140340a30c5Ssensoray-dev 		return -EBUSY;
1141340a30c5Ssensoray-dev 
114292cde477SDean Anderson 	mode = vc->mode;
1143314527acSHans Verkuil 	if (i & V4L2_STD_525_60) {
114492cde477SDean Anderson 		dprintk(vc->dev, 4, "%s 60 Hz\n", __func__);
11450aa77f6cSMauro Carvalho Chehab 		/* if changing format, reset frame decimation/intervals */
11460aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_NTSC) {
11470aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
11480aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_NTSC;
11490aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
11505e950fafSDean Anderson 			vc->width = LINE_SZ_4CIFS_NTSC;
11515e950fafSDean Anderson 			vc->height = NUM_LINES_4CIFS_NTSC * 2;
11520aa77f6cSMauro Carvalho Chehab 		}
1153314527acSHans Verkuil 	} else if (i & V4L2_STD_625_50) {
115492cde477SDean Anderson 		dprintk(vc->dev, 4, "%s 50 Hz\n", __func__);
11550aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_PAL) {
11560aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
11570aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_PAL;
11580aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
11595e950fafSDean Anderson 			vc->width = LINE_SZ_4CIFS_PAL;
11605e950fafSDean Anderson 			vc->height = NUM_LINES_4CIFS_PAL * 2;
11610aa77f6cSMauro Carvalho Chehab 		}
1162340a30c5Ssensoray-dev 	} else
1163340a30c5Ssensoray-dev 		return -EINVAL;
116492cde477SDean Anderson 	vc->std = i;
11650aa77f6cSMauro Carvalho Chehab 	if (mode.restart)
116692cde477SDean Anderson 		s2255_set_mode(vc, &mode);
1167340a30c5Ssensoray-dev 	return 0;
11680aa77f6cSMauro Carvalho Chehab }
11690aa77f6cSMauro Carvalho Chehab 
1170469af77aSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
1171469af77aSHans Verkuil {
1172340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
1173469af77aSHans Verkuil 
117492cde477SDean Anderson 	*i = vc->std;
1175469af77aSHans Verkuil 	return 0;
1176469af77aSHans Verkuil }
1177469af77aSHans Verkuil 
11780aa77f6cSMauro Carvalho Chehab /* Sensoray 2255 is a multiple channel capture device.
11790aa77f6cSMauro Carvalho Chehab    It does not have a "crossbar" of inputs.
11800aa77f6cSMauro Carvalho Chehab    We use one V4L device per channel. The user must
11810aa77f6cSMauro Carvalho Chehab    be aware that certain combinations are not allowed.
11820aa77f6cSMauro Carvalho Chehab    For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
11830aa77f6cSMauro Carvalho Chehab    at once in color(you can do full fps on 4 channels with greyscale.
11840aa77f6cSMauro Carvalho Chehab */
11850aa77f6cSMauro Carvalho Chehab static int vidioc_enum_input(struct file *file, void *priv,
11860aa77f6cSMauro Carvalho Chehab 			     struct v4l2_input *inp)
11870aa77f6cSMauro Carvalho Chehab {
1188340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
118992cde477SDean Anderson 	struct s2255_dev *dev = vc->dev;
11900aa77f6cSMauro Carvalho Chehab 	u32 status = 0;
119192cde477SDean Anderson 
11920aa77f6cSMauro Carvalho Chehab 	if (inp->index != 0)
11930aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
11940aa77f6cSMauro Carvalho Chehab 	inp->type = V4L2_INPUT_TYPE_CAMERA;
11950aa77f6cSMauro Carvalho Chehab 	inp->std = S2255_NORMS;
11960aa77f6cSMauro Carvalho Chehab 	inp->status = 0;
11970aa77f6cSMauro Carvalho Chehab 	if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
11980aa77f6cSMauro Carvalho Chehab 		int rc;
119992cde477SDean Anderson 		rc = s2255_cmd_status(vc, &status);
1200f5402007Ssensoray-dev 		dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n",
1201f5402007Ssensoray-dev 			rc, status);
12020aa77f6cSMauro Carvalho Chehab 		if (rc == 0)
12030aa77f6cSMauro Carvalho Chehab 			inp->status =  (status & 0x01) ? 0
12040aa77f6cSMauro Carvalho Chehab 				: V4L2_IN_ST_NO_SIGNAL;
12050aa77f6cSMauro Carvalho Chehab 	}
12060aa77f6cSMauro Carvalho Chehab 	switch (dev->pid) {
12070aa77f6cSMauro Carvalho Chehab 	case 0x2255:
12080aa77f6cSMauro Carvalho Chehab 	default:
12090aa77f6cSMauro Carvalho Chehab 		strlcpy(inp->name, "Composite", sizeof(inp->name));
12100aa77f6cSMauro Carvalho Chehab 		break;
12110aa77f6cSMauro Carvalho Chehab 	case 0x2257:
12125e950fafSDean Anderson 		strlcpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video",
12130aa77f6cSMauro Carvalho Chehab 			sizeof(inp->name));
12140aa77f6cSMauro Carvalho Chehab 		break;
12150aa77f6cSMauro Carvalho Chehab 	}
12160aa77f6cSMauro Carvalho Chehab 	return 0;
12170aa77f6cSMauro Carvalho Chehab }
12180aa77f6cSMauro Carvalho Chehab 
12190aa77f6cSMauro Carvalho Chehab static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
12200aa77f6cSMauro Carvalho Chehab {
12210aa77f6cSMauro Carvalho Chehab 	*i = 0;
12220aa77f6cSMauro Carvalho Chehab 	return 0;
12230aa77f6cSMauro Carvalho Chehab }
12240aa77f6cSMauro Carvalho Chehab static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
12250aa77f6cSMauro Carvalho Chehab {
12260aa77f6cSMauro Carvalho Chehab 	if (i > 0)
12270aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12280aa77f6cSMauro Carvalho Chehab 	return 0;
12290aa77f6cSMauro Carvalho Chehab }
12300aa77f6cSMauro Carvalho Chehab 
1231192f1e78SHans Verkuil static int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
12320aa77f6cSMauro Carvalho Chehab {
12335e950fafSDean Anderson 	struct s2255_vc *vc =
12345e950fafSDean Anderson 		container_of(ctrl->handler, struct s2255_vc, hdl);
12350aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
12365e950fafSDean Anderson 	mode = vc->mode;
12370aa77f6cSMauro Carvalho Chehab 	/* update the mode to the corresponding value */
12380aa77f6cSMauro Carvalho Chehab 	switch (ctrl->id) {
12390aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1240192f1e78SHans Verkuil 		mode.bright = ctrl->val;
12410aa77f6cSMauro Carvalho Chehab 		break;
12420aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1243192f1e78SHans Verkuil 		mode.contrast = ctrl->val;
12440aa77f6cSMauro Carvalho Chehab 		break;
12450aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_HUE:
1246192f1e78SHans Verkuil 		mode.hue = ctrl->val;
12470aa77f6cSMauro Carvalho Chehab 		break;
12480aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1249192f1e78SHans Verkuil 		mode.saturation = ctrl->val;
12500aa77f6cSMauro Carvalho Chehab 		break;
1251192f1e78SHans Verkuil 	case V4L2_CID_S2255_COLORFILTER:
12520aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_INPUT_TYPE;
1253192f1e78SHans Verkuil 		mode.color |= !ctrl->val << 16;
12540aa77f6cSMauro Carvalho Chehab 		break;
12557041dec7SHans Verkuil 	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
12565e950fafSDean Anderson 		vc->jpegqual = ctrl->val;
12577041dec7SHans Verkuil 		return 0;
12580aa77f6cSMauro Carvalho Chehab 	default:
12590aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12600aa77f6cSMauro Carvalho Chehab 	}
12610aa77f6cSMauro Carvalho Chehab 	mode.restart = 0;
12620aa77f6cSMauro Carvalho Chehab 	/* set mode here.  Note: stream does not need restarted.
12630aa77f6cSMauro Carvalho Chehab 	   some V4L programs restart stream unnecessarily
12640aa77f6cSMauro Carvalho Chehab 	   after a s_crtl.
12650aa77f6cSMauro Carvalho Chehab 	*/
12665e950fafSDean Anderson 	s2255_set_mode(vc, &mode);
12670aa77f6cSMauro Carvalho Chehab 	return 0;
12680aa77f6cSMauro Carvalho Chehab }
12690aa77f6cSMauro Carvalho Chehab 
12700aa77f6cSMauro Carvalho Chehab static int vidioc_g_jpegcomp(struct file *file, void *priv,
12710aa77f6cSMauro Carvalho Chehab 			 struct v4l2_jpegcompression *jc)
12720aa77f6cSMauro Carvalho Chehab {
1273340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
12747041dec7SHans Verkuil 
12757041dec7SHans Verkuil 	memset(jc, 0, sizeof(*jc));
12765e950fafSDean Anderson 	jc->quality = vc->jpegqual;
127792cde477SDean Anderson 	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
12780aa77f6cSMauro Carvalho Chehab 	return 0;
12790aa77f6cSMauro Carvalho Chehab }
12800aa77f6cSMauro Carvalho Chehab 
12810aa77f6cSMauro Carvalho Chehab static int vidioc_s_jpegcomp(struct file *file, void *priv,
1282d88aab53SHans Verkuil 			 const struct v4l2_jpegcompression *jc)
12830aa77f6cSMauro Carvalho Chehab {
1284340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
1285340a30c5Ssensoray-dev 
12860aa77f6cSMauro Carvalho Chehab 	if (jc->quality < 0 || jc->quality > 100)
12870aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12885e950fafSDean Anderson 	v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality);
128992cde477SDean Anderson 	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
12900aa77f6cSMauro Carvalho Chehab 	return 0;
12910aa77f6cSMauro Carvalho Chehab }
12920aa77f6cSMauro Carvalho Chehab 
12930aa77f6cSMauro Carvalho Chehab static int vidioc_g_parm(struct file *file, void *priv,
12940aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
12950aa77f6cSMauro Carvalho Chehab {
12960aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
1297340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
1298340a30c5Ssensoray-dev 
12990aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
13000aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13010aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
13025e950fafSDean Anderson 	sp->parm.capture.capturemode = vc->cap_parm.capturemode;
1303340a30c5Ssensoray-dev 	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
13045e950fafSDean Anderson 	def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000;
13055e950fafSDean Anderson 	def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000;
13060aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
13075e950fafSDean Anderson 	switch (vc->mode.fdec) {
13080aa77f6cSMauro Carvalho Chehab 	default:
13090aa77f6cSMauro Carvalho Chehab 	case FDEC_1:
13100aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
13110aa77f6cSMauro Carvalho Chehab 		break;
13120aa77f6cSMauro Carvalho Chehab 	case FDEC_2:
13130aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
13140aa77f6cSMauro Carvalho Chehab 		break;
13150aa77f6cSMauro Carvalho Chehab 	case FDEC_3:
13160aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
13170aa77f6cSMauro Carvalho Chehab 		break;
13180aa77f6cSMauro Carvalho Chehab 	case FDEC_5:
13190aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
13200aa77f6cSMauro Carvalho Chehab 		break;
13210aa77f6cSMauro Carvalho Chehab 	}
132292cde477SDean Anderson 	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n",
1323f5402007Ssensoray-dev 		__func__,
13240aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
13250aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
13260aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator);
13270aa77f6cSMauro Carvalho Chehab 	return 0;
13280aa77f6cSMauro Carvalho Chehab }
13290aa77f6cSMauro Carvalho Chehab 
13300aa77f6cSMauro Carvalho Chehab static int vidioc_s_parm(struct file *file, void *priv,
13310aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
13320aa77f6cSMauro Carvalho Chehab {
1333340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
13340aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
13350aa77f6cSMauro Carvalho Chehab 	int fdec = FDEC_1;
13360aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
13370aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
13380aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13395e950fafSDean Anderson 	mode = vc->mode;
13400aa77f6cSMauro Carvalho Chehab 	/* high quality capture mode requires a stream restart */
1341340a30c5Ssensoray-dev 	if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode)
1342340a30c5Ssensoray-dev 	    && vb2_is_streaming(&vc->vb_vidq))
13430aa77f6cSMauro Carvalho Chehab 		return -EBUSY;
13440aa77f6cSMauro Carvalho Chehab 	def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
13450aa77f6cSMauro Carvalho Chehab 	def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
13460aa77f6cSMauro Carvalho Chehab 	if (def_dem != sp->parm.capture.timeperframe.denominator)
13470aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
13480aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= def_num)
13490aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
13500aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
13510aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
13520aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_2;
13530aa77f6cSMauro Carvalho Chehab 	} else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
13540aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
13550aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_3;
13560aa77f6cSMauro Carvalho Chehab 	} else {
13570aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
13580aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_5;
13590aa77f6cSMauro Carvalho Chehab 	}
13600aa77f6cSMauro Carvalho Chehab 	mode.fdec = fdec;
13610aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
1362340a30c5Ssensoray-dev 	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
13635e950fafSDean Anderson 	s2255_set_mode(vc, &mode);
136492cde477SDean Anderson 	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
13650aa77f6cSMauro Carvalho Chehab 		__func__,
13660aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
13670aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
13680aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator, fdec);
13690aa77f6cSMauro Carvalho Chehab 	return 0;
13700aa77f6cSMauro Carvalho Chehab }
13710aa77f6cSMauro Carvalho Chehab 
137205e5d44bSHans Verkuil #define NUM_SIZE_ENUMS 3
137305e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete ntsc_sizes[] = {
137405e5d44bSHans Verkuil 	{ 640, 480 },
137505e5d44bSHans Verkuil 	{ 640, 240 },
137605e5d44bSHans Verkuil 	{ 320, 240 },
137705e5d44bSHans Verkuil };
137805e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete pal_sizes[] = {
137905e5d44bSHans Verkuil 	{ 704, 576 },
138005e5d44bSHans Verkuil 	{ 704, 288 },
138105e5d44bSHans Verkuil 	{ 352, 288 },
138205e5d44bSHans Verkuil };
138305e5d44bSHans Verkuil 
138405e5d44bSHans Verkuil static int vidioc_enum_framesizes(struct file *file, void *priv,
138505e5d44bSHans Verkuil 			    struct v4l2_frmsizeenum *fe)
138605e5d44bSHans Verkuil {
1387340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
13885e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
138905e5d44bSHans Verkuil 	const struct s2255_fmt *fmt;
139005e5d44bSHans Verkuil 
139105e5d44bSHans Verkuil 	if (fe->index >= NUM_SIZE_ENUMS)
139205e5d44bSHans Verkuil 		return -EINVAL;
139305e5d44bSHans Verkuil 
139405e5d44bSHans Verkuil 	fmt = format_by_fourcc(fe->pixel_format);
139505e5d44bSHans Verkuil 	if (fmt == NULL)
139605e5d44bSHans Verkuil 		return -EINVAL;
139705e5d44bSHans Verkuil 	fe->type = V4L2_FRMSIZE_TYPE_DISCRETE;
139805e5d44bSHans Verkuil 	fe->discrete = is_ntsc ?  ntsc_sizes[fe->index] : pal_sizes[fe->index];
139905e5d44bSHans Verkuil 	return 0;
140005e5d44bSHans Verkuil }
140105e5d44bSHans Verkuil 
14020aa77f6cSMauro Carvalho Chehab static int vidioc_enum_frameintervals(struct file *file, void *priv,
14030aa77f6cSMauro Carvalho Chehab 			    struct v4l2_frmivalenum *fe)
14040aa77f6cSMauro Carvalho Chehab {
1405340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
140605e5d44bSHans Verkuil 	const struct s2255_fmt *fmt;
140705e5d44bSHans Verkuil 	const struct v4l2_frmsize_discrete *sizes;
14085e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
14090aa77f6cSMauro Carvalho Chehab #define NUM_FRAME_ENUMS 4
14100aa77f6cSMauro Carvalho Chehab 	int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
141105e5d44bSHans Verkuil 	int i;
141205e5d44bSHans Verkuil 
1413199ab8feSMauro Carvalho Chehab 	if (fe->index >= NUM_FRAME_ENUMS)
14140aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
141505e5d44bSHans Verkuil 
141605e5d44bSHans Verkuil 	fmt = format_by_fourcc(fe->pixel_format);
141705e5d44bSHans Verkuil 	if (fmt == NULL)
14180aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
141905e5d44bSHans Verkuil 
142005e5d44bSHans Verkuil 	sizes = is_ntsc ? ntsc_sizes : pal_sizes;
142105e5d44bSHans Verkuil 	for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++)
142205e5d44bSHans Verkuil 		if (fe->width == sizes->width &&
142305e5d44bSHans Verkuil 		    fe->height == sizes->height)
14240aa77f6cSMauro Carvalho Chehab 			break;
142505e5d44bSHans Verkuil 	if (i == NUM_SIZE_ENUMS)
14260aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
142705e5d44bSHans Verkuil 
14280aa77f6cSMauro Carvalho Chehab 	fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
14290aa77f6cSMauro Carvalho Chehab 	fe->discrete.denominator = is_ntsc ? 30000 : 25000;
14300aa77f6cSMauro Carvalho Chehab 	fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
143192cde477SDean Anderson 	dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__,
1432f5402007Ssensoray-dev 		fe->discrete.numerator,
14330aa77f6cSMauro Carvalho Chehab 		fe->discrete.denominator);
14340aa77f6cSMauro Carvalho Chehab 	return 0;
14350aa77f6cSMauro Carvalho Chehab }
14360aa77f6cSMauro Carvalho Chehab 
1437340a30c5Ssensoray-dev static int s2255_open(struct file *file)
14380aa77f6cSMauro Carvalho Chehab {
14395e950fafSDean Anderson 	struct s2255_vc *vc = video_drvdata(file);
1440340a30c5Ssensoray-dev 	struct s2255_dev *dev = vc->dev;
14410aa77f6cSMauro Carvalho Chehab 	int state;
1442340a30c5Ssensoray-dev 	int rc = 0;
1443340a30c5Ssensoray-dev 
1444340a30c5Ssensoray-dev 	rc = v4l2_fh_open(file);
1445340a30c5Ssensoray-dev 	if (rc != 0)
1446340a30c5Ssensoray-dev 		return rc;
1447340a30c5Ssensoray-dev 
1448340a30c5Ssensoray-dev 	dprintk(dev, 1, "s2255: %s\n", __func__);
14490aa77f6cSMauro Carvalho Chehab 	state = atomic_read(&dev->fw_data->fw_state);
14500aa77f6cSMauro Carvalho Chehab 	switch (state) {
14510aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
14520aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
14530aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
14540aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&dev->udev->dev,
14550aa77f6cSMauro Carvalho Chehab 			"firmware load failed. retrying.\n");
14560aa77f6cSMauro Carvalho Chehab 		s2255_fwload_start(dev, 1);
14570aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
14580aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
14590aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
14600aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
14610aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
14620aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
14630aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
14640aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
14650aa77f6cSMauro Carvalho Chehab 		break;
14660aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
14670aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
14680aa77f6cSMauro Carvalho Chehab 		/* give S2255_LOAD_TIMEOUT time for firmware to load in case
14690aa77f6cSMauro Carvalho Chehab 		   driver loaded and then device immediately opened */
1470f5402007Ssensoray-dev 		pr_info("%s waiting for firmware load\n", __func__);
14710aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
14720aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
14730aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
14740aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
14750aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
14760aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
14770aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
14780aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
14790aa77f6cSMauro Carvalho Chehab 		break;
14800aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
14810aa77f6cSMauro Carvalho Chehab 	default:
14820aa77f6cSMauro Carvalho Chehab 		break;
14830aa77f6cSMauro Carvalho Chehab 	}
14840aa77f6cSMauro Carvalho Chehab 	/* state may have changed in above switch statement */
14850aa77f6cSMauro Carvalho Chehab 	switch (state) {
14860aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
14870aa77f6cSMauro Carvalho Chehab 		break;
14880aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
1489f5402007Ssensoray-dev 		pr_info("2255 firmware load failed.\n");
14900aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
14910aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
1492f5402007Ssensoray-dev 		pr_info("%s: disconnecting\n", __func__);
14930aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
14940aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
14950aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
1496f5402007Ssensoray-dev 		pr_info("%s: firmware not loaded, please retry\n",
14970aa77f6cSMauro Carvalho Chehab 			__func__);
14980aa77f6cSMauro Carvalho Chehab 		/*
14990aa77f6cSMauro Carvalho Chehab 		 * Timeout on firmware load means device unusable.
15000aa77f6cSMauro Carvalho Chehab 		 * Set firmware failure state.
15010aa77f6cSMauro Carvalho Chehab 		 * On next s2255_open the firmware will be reloaded.
15020aa77f6cSMauro Carvalho Chehab 		 */
15030aa77f6cSMauro Carvalho Chehab 		atomic_set(&dev->fw_data->fw_state,
15040aa77f6cSMauro Carvalho Chehab 			   S2255_FW_FAILED);
15050aa77f6cSMauro Carvalho Chehab 		return -EAGAIN;
15060aa77f6cSMauro Carvalho Chehab 	default:
1507f5402007Ssensoray-dev 		pr_info("%s: unknown state\n", __func__);
15080aa77f6cSMauro Carvalho Chehab 		return -EFAULT;
15090aa77f6cSMauro Carvalho Chehab 	}
15105e950fafSDean Anderson 	if (!vc->configured) {
15110aa77f6cSMauro Carvalho Chehab 		/* configure channel to default state */
15125e950fafSDean Anderson 		vc->fmt = &formats[0];
15135e950fafSDean Anderson 		s2255_set_mode(vc, &vc->mode);
15145e950fafSDean Anderson 		vc->configured = 1;
15150aa77f6cSMauro Carvalho Chehab 	}
15160aa77f6cSMauro Carvalho Chehab 	return 0;
15170aa77f6cSMauro Carvalho Chehab }
15180aa77f6cSMauro Carvalho Chehab 
15190aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev)
15200aa77f6cSMauro Carvalho Chehab {
1521f5402007Ssensoray-dev 	dprintk(dev, 1, "%s", __func__);
15220aa77f6cSMauro Carvalho Chehab 	/* board shutdown stops the read pipe if it is running */
15230aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
15240aa77f6cSMauro Carvalho Chehab 	/* make sure firmware still not trying to load */
15259f6be2bcSKirill Tkhai 	del_timer_sync(&dev->timer);  /* only started in .probe and .open */
15260aa77f6cSMauro Carvalho Chehab 	if (dev->fw_data->fw_urb) {
15270aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(dev->fw_data->fw_urb);
15280aa77f6cSMauro Carvalho Chehab 		usb_free_urb(dev->fw_data->fw_urb);
15290aa77f6cSMauro Carvalho Chehab 		dev->fw_data->fw_urb = NULL;
15300aa77f6cSMauro Carvalho Chehab 	}
15310aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
15320aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
15330aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
15340aa77f6cSMauro Carvalho Chehab 	/* reset the DSP so firmware can be reloaded next time */
15350aa77f6cSMauro Carvalho Chehab 	s2255_reset_dsppower(dev);
15360aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
15370aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
15380aa77f6cSMauro Carvalho Chehab 	v4l2_device_unregister(&dev->v4l2_dev);
153947d8c881SDean Anderson 	kfree(dev->cmdbuf);
15400aa77f6cSMauro Carvalho Chehab 	kfree(dev);
15410aa77f6cSMauro Carvalho Chehab }
15420aa77f6cSMauro Carvalho Chehab 
15430aa77f6cSMauro Carvalho Chehab static const struct v4l2_file_operations s2255_fops_v4l = {
15440aa77f6cSMauro Carvalho Chehab 	.owner = THIS_MODULE,
15450aa77f6cSMauro Carvalho Chehab 	.open = s2255_open,
1546340a30c5Ssensoray-dev 	.release = vb2_fop_release,
1547340a30c5Ssensoray-dev 	.poll = vb2_fop_poll,
15480aa77f6cSMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,	/* V4L2 ioctl handler */
1549340a30c5Ssensoray-dev 	.mmap = vb2_fop_mmap,
1550340a30c5Ssensoray-dev 	.read = vb2_fop_read,
15510aa77f6cSMauro Carvalho Chehab };
15520aa77f6cSMauro Carvalho Chehab 
15530aa77f6cSMauro Carvalho Chehab static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
15540aa77f6cSMauro Carvalho Chehab 	.vidioc_querycap = vidioc_querycap,
15550aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
15560aa77f6cSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
15570aa77f6cSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
15580aa77f6cSMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
1559340a30c5Ssensoray-dev 	.vidioc_reqbufs = vb2_ioctl_reqbufs,
1560340a30c5Ssensoray-dev 	.vidioc_querybuf = vb2_ioctl_querybuf,
1561340a30c5Ssensoray-dev 	.vidioc_qbuf = vb2_ioctl_qbuf,
1562340a30c5Ssensoray-dev 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
15630aa77f6cSMauro Carvalho Chehab 	.vidioc_s_std = vidioc_s_std,
1564469af77aSHans Verkuil 	.vidioc_g_std = vidioc_g_std,
15650aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_input = vidioc_enum_input,
15660aa77f6cSMauro Carvalho Chehab 	.vidioc_g_input = vidioc_g_input,
15670aa77f6cSMauro Carvalho Chehab 	.vidioc_s_input = vidioc_s_input,
1568340a30c5Ssensoray-dev 	.vidioc_streamon = vb2_ioctl_streamon,
1569340a30c5Ssensoray-dev 	.vidioc_streamoff = vb2_ioctl_streamoff,
15700aa77f6cSMauro Carvalho Chehab 	.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
15710aa77f6cSMauro Carvalho Chehab 	.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
15720aa77f6cSMauro Carvalho Chehab 	.vidioc_s_parm = vidioc_s_parm,
15730aa77f6cSMauro Carvalho Chehab 	.vidioc_g_parm = vidioc_g_parm,
157405e5d44bSHans Verkuil 	.vidioc_enum_framesizes = vidioc_enum_framesizes,
15750aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
157644d06d82SHans Verkuil 	.vidioc_log_status  = v4l2_ctrl_log_status,
157744d06d82SHans Verkuil 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
157844d06d82SHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
15790aa77f6cSMauro Carvalho Chehab };
15800aa77f6cSMauro Carvalho Chehab 
15810aa77f6cSMauro Carvalho Chehab static void s2255_video_device_release(struct video_device *vdev)
15820aa77f6cSMauro Carvalho Chehab {
15830aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
15845e950fafSDean Anderson 	struct s2255_vc *vc =
15855e950fafSDean Anderson 		container_of(vdev, struct s2255_vc, vdev);
1586192f1e78SHans Verkuil 
1587f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, chnls: %d\n", __func__,
15880aa77f6cSMauro Carvalho Chehab 		atomic_read(&dev->num_channels));
1589192f1e78SHans Verkuil 
15905e950fafSDean Anderson 	v4l2_ctrl_handler_free(&vc->hdl);
1591f5402007Ssensoray-dev 
15920aa77f6cSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->num_channels))
15930aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
15940aa77f6cSMauro Carvalho Chehab 	return;
15950aa77f6cSMauro Carvalho Chehab }
15960aa77f6cSMauro Carvalho Chehab 
15970aa77f6cSMauro Carvalho Chehab static struct video_device template = {
15980aa77f6cSMauro Carvalho Chehab 	.name = "s2255v",
15990aa77f6cSMauro Carvalho Chehab 	.fops = &s2255_fops_v4l,
16000aa77f6cSMauro Carvalho Chehab 	.ioctl_ops = &s2255_ioctl_ops,
16010aa77f6cSMauro Carvalho Chehab 	.release = s2255_video_device_release,
16020aa77f6cSMauro Carvalho Chehab 	.tvnorms = S2255_NORMS,
16030aa77f6cSMauro Carvalho Chehab };
16040aa77f6cSMauro Carvalho Chehab 
1605192f1e78SHans Verkuil static const struct v4l2_ctrl_ops s2255_ctrl_ops = {
1606192f1e78SHans Verkuil 	.s_ctrl = s2255_s_ctrl,
1607192f1e78SHans Verkuil };
1608192f1e78SHans Verkuil 
1609192f1e78SHans Verkuil static const struct v4l2_ctrl_config color_filter_ctrl = {
1610192f1e78SHans Verkuil 	.ops = &s2255_ctrl_ops,
1611192f1e78SHans Verkuil 	.name = "Color Filter",
1612192f1e78SHans Verkuil 	.id = V4L2_CID_S2255_COLORFILTER,
1613192f1e78SHans Verkuil 	.type = V4L2_CTRL_TYPE_BOOLEAN,
1614192f1e78SHans Verkuil 	.max = 1,
1615192f1e78SHans Verkuil 	.step = 1,
1616192f1e78SHans Verkuil 	.def = 1,
1617192f1e78SHans Verkuil };
1618192f1e78SHans Verkuil 
16190aa77f6cSMauro Carvalho Chehab static int s2255_probe_v4l(struct s2255_dev *dev)
16200aa77f6cSMauro Carvalho Chehab {
16210aa77f6cSMauro Carvalho Chehab 	int ret;
16220aa77f6cSMauro Carvalho Chehab 	int i;
16230aa77f6cSMauro Carvalho Chehab 	int cur_nr = video_nr;
16245e950fafSDean Anderson 	struct s2255_vc *vc;
1625340a30c5Ssensoray-dev 	struct vb2_queue *q;
1626340a30c5Ssensoray-dev 
16270aa77f6cSMauro Carvalho Chehab 	ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
16280aa77f6cSMauro Carvalho Chehab 	if (ret)
16290aa77f6cSMauro Carvalho Chehab 		return ret;
16300aa77f6cSMauro Carvalho Chehab 	/* initialize all video 4 linux */
16310aa77f6cSMauro Carvalho Chehab 	/* register 4 video devices */
16320aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
16335e950fafSDean Anderson 		vc = &dev->vc[i];
16345e950fafSDean Anderson 		INIT_LIST_HEAD(&vc->buf_list);
1635192f1e78SHans Verkuil 
16365e950fafSDean Anderson 		v4l2_ctrl_handler_init(&vc->hdl, 6);
16375e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1638192f1e78SHans Verkuil 				V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT);
16395e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1640192f1e78SHans Verkuil 				V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST);
16415e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1642192f1e78SHans Verkuil 				V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION);
16435e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1644192f1e78SHans Verkuil 				V4L2_CID_HUE, 0, 255, 1, DEF_HUE);
16455e950fafSDean Anderson 		vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl,
16467041dec7SHans Verkuil 				&s2255_ctrl_ops,
16477041dec7SHans Verkuil 				V4L2_CID_JPEG_COMPRESSION_QUALITY,
16487041dec7SHans Verkuil 				0, 100, 1, S2255_DEF_JPEG_QUAL);
1649192f1e78SHans Verkuil 		if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER &&
16505e950fafSDean Anderson 		    (dev->pid != 0x2257 || vc->idx <= 1))
16515e950fafSDean Anderson 			v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl,
1652f5402007Ssensoray-dev 					     NULL);
16535e950fafSDean Anderson 		if (vc->hdl.error) {
16545e950fafSDean Anderson 			ret = vc->hdl.error;
16555e950fafSDean Anderson 			v4l2_ctrl_handler_free(&vc->hdl);
1656192f1e78SHans Verkuil 			dev_err(&dev->udev->dev, "couldn't register control\n");
1657192f1e78SHans Verkuil 			break;
1658192f1e78SHans Verkuil 		}
1659340a30c5Ssensoray-dev 		q = &vc->vb_vidq;
1660340a30c5Ssensoray-dev 		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1661340a30c5Ssensoray-dev 		q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR;
1662340a30c5Ssensoray-dev 		q->drv_priv = vc;
1663340a30c5Ssensoray-dev 		q->lock = &vc->vb_lock;
1664340a30c5Ssensoray-dev 		q->buf_struct_size = sizeof(struct s2255_buffer);
1665340a30c5Ssensoray-dev 		q->mem_ops = &vb2_vmalloc_memops;
1666340a30c5Ssensoray-dev 		q->ops = &s2255_video_qops;
1667ade48681SSakari Ailus 		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1668340a30c5Ssensoray-dev 		ret = vb2_queue_init(q);
1669340a30c5Ssensoray-dev 		if (ret != 0) {
1670340a30c5Ssensoray-dev 			dev_err(&dev->udev->dev,
1671340a30c5Ssensoray-dev 				"%s vb2_queue_init 0x%x\n", __func__, ret);
1672340a30c5Ssensoray-dev 			break;
1673340a30c5Ssensoray-dev 		}
1674340a30c5Ssensoray-dev 		/* register video devices */
16755e950fafSDean Anderson 		vc->vdev = template;
1676340a30c5Ssensoray-dev 		vc->vdev.queue = q;
16775e950fafSDean Anderson 		vc->vdev.ctrl_handler = &vc->hdl;
16785e950fafSDean Anderson 		vc->vdev.lock = &dev->lock;
16795e950fafSDean Anderson 		vc->vdev.v4l2_dev = &dev->v4l2_dev;
16805e950fafSDean Anderson 		video_set_drvdata(&vc->vdev, vc);
16810aa77f6cSMauro Carvalho Chehab 		if (video_nr == -1)
16825e950fafSDean Anderson 			ret = video_register_device(&vc->vdev,
16830aa77f6cSMauro Carvalho Chehab 						    VFL_TYPE_GRABBER,
16840aa77f6cSMauro Carvalho Chehab 						    video_nr);
16850aa77f6cSMauro Carvalho Chehab 		else
16865e950fafSDean Anderson 			ret = video_register_device(&vc->vdev,
16870aa77f6cSMauro Carvalho Chehab 						    VFL_TYPE_GRABBER,
16880aa77f6cSMauro Carvalho Chehab 						    cur_nr + i);
16890aa77f6cSMauro Carvalho Chehab 
16900aa77f6cSMauro Carvalho Chehab 		if (ret) {
16910aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev,
16920aa77f6cSMauro Carvalho Chehab 				"failed to register video device!\n");
16930aa77f6cSMauro Carvalho Chehab 			break;
16940aa77f6cSMauro Carvalho Chehab 		}
16950aa77f6cSMauro Carvalho Chehab 		atomic_inc(&dev->num_channels);
16960aa77f6cSMauro Carvalho Chehab 		v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
16975e950fafSDean Anderson 			  video_device_node_name(&vc->vdev));
16980aa77f6cSMauro Carvalho Chehab 
16990aa77f6cSMauro Carvalho Chehab 	}
1700f5402007Ssensoray-dev 	pr_info("Sensoray 2255 V4L driver Revision: %s\n",
17010aa77f6cSMauro Carvalho Chehab 		S2255_VERSION);
17020aa77f6cSMauro Carvalho Chehab 	/* if no channels registered, return error and probe will fail*/
17030aa77f6cSMauro Carvalho Chehab 	if (atomic_read(&dev->num_channels) == 0) {
17040aa77f6cSMauro Carvalho Chehab 		v4l2_device_unregister(&dev->v4l2_dev);
17050aa77f6cSMauro Carvalho Chehab 		return ret;
17060aa77f6cSMauro Carvalho Chehab 	}
17070aa77f6cSMauro Carvalho Chehab 	if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
1708f5402007Ssensoray-dev 		pr_warn("s2255: Not all channels available.\n");
17090aa77f6cSMauro Carvalho Chehab 	return 0;
17100aa77f6cSMauro Carvalho Chehab }
17110aa77f6cSMauro Carvalho Chehab 
17120aa77f6cSMauro Carvalho Chehab /* this function moves the usb stream read pipe data
17130aa77f6cSMauro Carvalho Chehab  * into the system buffers.
17140aa77f6cSMauro Carvalho Chehab  * returns 0 on success, EAGAIN if more data to process( call this
17150aa77f6cSMauro Carvalho Chehab  * function again).
17160aa77f6cSMauro Carvalho Chehab  *
17170aa77f6cSMauro Carvalho Chehab  * Received frame structure:
17180aa77f6cSMauro Carvalho Chehab  * bytes 0-3:  marker : 0x2255DA4AL (S2255_MARKER_FRAME)
17190aa77f6cSMauro Carvalho Chehab  * bytes 4-7:  channel: 0-3
17200aa77f6cSMauro Carvalho Chehab  * bytes 8-11: payload size:  size of the frame
17210aa77f6cSMauro Carvalho Chehab  * bytes 12-payloadsize+12:  frame data
17220aa77f6cSMauro Carvalho Chehab  */
17230aa77f6cSMauro Carvalho Chehab static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
17240aa77f6cSMauro Carvalho Chehab {
17250aa77f6cSMauro Carvalho Chehab 	char *pdest;
17260aa77f6cSMauro Carvalho Chehab 	u32 offset = 0;
17270aa77f6cSMauro Carvalho Chehab 	int bframe = 0;
17280aa77f6cSMauro Carvalho Chehab 	char *psrc;
17290aa77f6cSMauro Carvalho Chehab 	unsigned long copy_size;
17300aa77f6cSMauro Carvalho Chehab 	unsigned long size;
17310aa77f6cSMauro Carvalho Chehab 	s32 idx = -1;
17320aa77f6cSMauro Carvalho Chehab 	struct s2255_framei *frm;
17330aa77f6cSMauro Carvalho Chehab 	unsigned char *pdata;
17345e950fafSDean Anderson 	struct s2255_vc *vc;
1735f5402007Ssensoray-dev 	dprintk(dev, 100, "buffer to user\n");
17365e950fafSDean Anderson 	vc = &dev->vc[dev->cc];
17375e950fafSDean Anderson 	idx = vc->cur_frame;
17385e950fafSDean Anderson 	frm = &vc->buffer.frame[idx];
17390aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
17400aa77f6cSMauro Carvalho Chehab 		int jj;
17410aa77f6cSMauro Carvalho Chehab 		unsigned int cc;
17420aa77f6cSMauro Carvalho Chehab 		__le32 *pdword; /*data from dsp is little endian */
17430aa77f6cSMauro Carvalho Chehab 		int payload;
17440aa77f6cSMauro Carvalho Chehab 		/* search for marker codes */
17450aa77f6cSMauro Carvalho Chehab 		pdata = (unsigned char *)pipe_info->transfer_buffer;
17460aa77f6cSMauro Carvalho Chehab 		pdword = (__le32 *)pdata;
17470aa77f6cSMauro Carvalho Chehab 		for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
17480aa77f6cSMauro Carvalho Chehab 			switch (*pdword) {
17490aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_FRAME:
1750f5402007Ssensoray-dev 				dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
1751f5402007Ssensoray-dev 					jj, pdata[0], pdata[1]);
17520aa77f6cSMauro Carvalho Chehab 				offset = jj + PREFIX_SIZE;
17530aa77f6cSMauro Carvalho Chehab 				bframe = 1;
17540aa77f6cSMauro Carvalho Chehab 				cc = le32_to_cpu(pdword[1]);
17550aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS) {
1756f5402007Ssensoray-dev 					dprintk(dev, 0,
17570aa77f6cSMauro Carvalho Chehab 						"bad channel\n");
17580aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
17590aa77f6cSMauro Carvalho Chehab 				}
17600aa77f6cSMauro Carvalho Chehab 				/* reverse it */
17610aa77f6cSMauro Carvalho Chehab 				dev->cc = G_chnmap[cc];
17625e950fafSDean Anderson 				vc = &dev->vc[dev->cc];
17630aa77f6cSMauro Carvalho Chehab 				payload =  le32_to_cpu(pdword[3]);
17645e950fafSDean Anderson 				if (payload > vc->req_image_size) {
17655e950fafSDean Anderson 					vc->bad_payload++;
17660aa77f6cSMauro Carvalho Chehab 					/* discard the bad frame */
17670aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
17680aa77f6cSMauro Carvalho Chehab 				}
17695e950fafSDean Anderson 				vc->pkt_size = payload;
17705e950fafSDean Anderson 				vc->jpg_size = le32_to_cpu(pdword[4]);
17710aa77f6cSMauro Carvalho Chehab 				break;
17720aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_RESPONSE:
17730aa77f6cSMauro Carvalho Chehab 
17740aa77f6cSMauro Carvalho Chehab 				pdata += DEF_USB_BLOCK;
17750aa77f6cSMauro Carvalho Chehab 				jj += DEF_USB_BLOCK;
17760aa77f6cSMauro Carvalho Chehab 				if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
17770aa77f6cSMauro Carvalho Chehab 					break;
17780aa77f6cSMauro Carvalho Chehab 				cc = G_chnmap[le32_to_cpu(pdword[1])];
17790aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS)
17800aa77f6cSMauro Carvalho Chehab 					break;
17815e950fafSDean Anderson 				vc = &dev->vc[cc];
17820aa77f6cSMauro Carvalho Chehab 				switch (pdword[2]) {
17830aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_SETMODE:
17840aa77f6cSMauro Carvalho Chehab 					/* check if channel valid */
17850aa77f6cSMauro Carvalho Chehab 					/* set mode ready */
17865e950fafSDean Anderson 					vc->setmode_ready = 1;
17875e950fafSDean Anderson 					wake_up(&vc->wait_setmode);
1788f5402007Ssensoray-dev 					dprintk(dev, 5, "setmode rdy %d\n", cc);
17890aa77f6cSMauro Carvalho Chehab 					break;
17900aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_FW:
17910aa77f6cSMauro Carvalho Chehab 					dev->chn_ready |= (1 << cc);
17920aa77f6cSMauro Carvalho Chehab 					if ((dev->chn_ready & 0x0f) != 0x0f)
17930aa77f6cSMauro Carvalho Chehab 						break;
17940aa77f6cSMauro Carvalho Chehab 					/* all channels ready */
1795f5402007Ssensoray-dev 					pr_info("s2255: fw loaded\n");
17960aa77f6cSMauro Carvalho Chehab 					atomic_set(&dev->fw_data->fw_state,
17970aa77f6cSMauro Carvalho Chehab 						   S2255_FW_SUCCESS);
17980aa77f6cSMauro Carvalho Chehab 					wake_up(&dev->fw_data->wait_fw);
17990aa77f6cSMauro Carvalho Chehab 					break;
18000aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_STATUS:
18015e950fafSDean Anderson 					vc->vidstatus = le32_to_cpu(pdword[3]);
18025e950fafSDean Anderson 					vc->vidstatus_ready = 1;
18035e950fafSDean Anderson 					wake_up(&vc->wait_vidstatus);
1804f5402007Ssensoray-dev 					dprintk(dev, 5, "vstat %x chan %d\n",
18050aa77f6cSMauro Carvalho Chehab 						le32_to_cpu(pdword[3]), cc);
18060aa77f6cSMauro Carvalho Chehab 					break;
18070aa77f6cSMauro Carvalho Chehab 				default:
1808f5402007Ssensoray-dev 					pr_info("s2255 unknown resp\n");
18090aa77f6cSMauro Carvalho Chehab 				}
18100aa77f6cSMauro Carvalho Chehab 			default:
18110aa77f6cSMauro Carvalho Chehab 				pdata++;
18120aa77f6cSMauro Carvalho Chehab 				break;
18130aa77f6cSMauro Carvalho Chehab 			}
18140aa77f6cSMauro Carvalho Chehab 			if (bframe)
18150aa77f6cSMauro Carvalho Chehab 				break;
18160aa77f6cSMauro Carvalho Chehab 		} /* for */
18170aa77f6cSMauro Carvalho Chehab 		if (!bframe)
18180aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
18190aa77f6cSMauro Carvalho Chehab 	}
18205e950fafSDean Anderson 	vc = &dev->vc[dev->cc];
18215e950fafSDean Anderson 	idx = vc->cur_frame;
18225e950fafSDean Anderson 	frm = &vc->buffer.frame[idx];
18230aa77f6cSMauro Carvalho Chehab 	/* search done.  now find out if should be acquiring on this channel */
1824340a30c5Ssensoray-dev 	if (!vb2_is_streaming(&vc->vb_vidq)) {
18250aa77f6cSMauro Carvalho Chehab 		/* we found a frame, but this channel is turned off */
18260aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
18270aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
18280aa77f6cSMauro Carvalho Chehab 	}
18290aa77f6cSMauro Carvalho Chehab 
18300aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
18310aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_FRAME;
18320aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
18330aa77f6cSMauro Carvalho Chehab 	}
18340aa77f6cSMauro Carvalho Chehab 
18350aa77f6cSMauro Carvalho Chehab 	/* skip the marker 512 bytes (and offset if out of sync) */
18360aa77f6cSMauro Carvalho Chehab 	psrc = (u8 *)pipe_info->transfer_buffer + offset;
18370aa77f6cSMauro Carvalho Chehab 
18380aa77f6cSMauro Carvalho Chehab 
18390aa77f6cSMauro Carvalho Chehab 	if (frm->lpvbits == NULL) {
1840f5402007Ssensoray-dev 		dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d",
18410aa77f6cSMauro Carvalho Chehab 			frm, dev, dev->cc, idx);
18420aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
18430aa77f6cSMauro Carvalho Chehab 	}
18440aa77f6cSMauro Carvalho Chehab 
18450aa77f6cSMauro Carvalho Chehab 	pdest = frm->lpvbits + frm->cur_size;
18460aa77f6cSMauro Carvalho Chehab 
18470aa77f6cSMauro Carvalho Chehab 	copy_size = (pipe_info->cur_transfer_size - offset);
18480aa77f6cSMauro Carvalho Chehab 
18495e950fafSDean Anderson 	size = vc->pkt_size - PREFIX_SIZE;
18500aa77f6cSMauro Carvalho Chehab 
18510aa77f6cSMauro Carvalho Chehab 	/* sanity check on pdest */
18525e950fafSDean Anderson 	if ((copy_size + frm->cur_size) < vc->req_image_size)
18530aa77f6cSMauro Carvalho Chehab 		memcpy(pdest, psrc, copy_size);
18540aa77f6cSMauro Carvalho Chehab 
18550aa77f6cSMauro Carvalho Chehab 	frm->cur_size += copy_size;
1856f5402007Ssensoray-dev 	dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size);
18570aa77f6cSMauro Carvalho Chehab 
18580aa77f6cSMauro Carvalho Chehab 	if (frm->cur_size >= size) {
1859f5402007Ssensoray-dev 		dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n",
18600aa77f6cSMauro Carvalho Chehab 			dev->cc, idx);
18615e950fafSDean Anderson 		vc->last_frame = vc->cur_frame;
18625e950fafSDean Anderson 		vc->cur_frame++;
18630aa77f6cSMauro Carvalho Chehab 		/* end of system frame ring buffer, start at zero */
18645e950fafSDean Anderson 		if ((vc->cur_frame == SYS_FRAMES) ||
18655e950fafSDean Anderson 		    (vc->cur_frame == vc->buffer.dwFrames))
18665e950fafSDean Anderson 			vc->cur_frame = 0;
18670aa77f6cSMauro Carvalho Chehab 		/* frame ready */
1868340a30c5Ssensoray-dev 		if (vb2_is_streaming(&vc->vb_vidq))
18695e950fafSDean Anderson 			s2255_got_frame(vc, vc->jpg_size);
18705e950fafSDean Anderson 		vc->frame_count++;
18710aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
18720aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
18730aa77f6cSMauro Carvalho Chehab 
18740aa77f6cSMauro Carvalho Chehab 	}
18750aa77f6cSMauro Carvalho Chehab 	/* done successfully */
18760aa77f6cSMauro Carvalho Chehab 	return 0;
18770aa77f6cSMauro Carvalho Chehab }
18780aa77f6cSMauro Carvalho Chehab 
18790aa77f6cSMauro Carvalho Chehab static void s2255_read_video_callback(struct s2255_dev *dev,
18800aa77f6cSMauro Carvalho Chehab 				      struct s2255_pipeinfo *pipe_info)
18810aa77f6cSMauro Carvalho Chehab {
18820aa77f6cSMauro Carvalho Chehab 	int res;
1883f5402007Ssensoray-dev 	dprintk(dev, 50, "callback read video\n");
18840aa77f6cSMauro Carvalho Chehab 
18850aa77f6cSMauro Carvalho Chehab 	if (dev->cc >= MAX_CHANNELS) {
18860aa77f6cSMauro Carvalho Chehab 		dev->cc = 0;
18870aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid channel\n");
18880aa77f6cSMauro Carvalho Chehab 		return;
18890aa77f6cSMauro Carvalho Chehab 	}
18900aa77f6cSMauro Carvalho Chehab 	/* otherwise copy to the system buffers */
18910aa77f6cSMauro Carvalho Chehab 	res = save_frame(dev, pipe_info);
18920aa77f6cSMauro Carvalho Chehab 	if (res != 0)
1893f5402007Ssensoray-dev 		dprintk(dev, 4, "s2255: read callback failed\n");
18940aa77f6cSMauro Carvalho Chehab 
1895f5402007Ssensoray-dev 	dprintk(dev, 50, "callback read video done\n");
18960aa77f6cSMauro Carvalho Chehab 	return;
18970aa77f6cSMauro Carvalho Chehab }
18980aa77f6cSMauro Carvalho Chehab 
18990aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
19000aa77f6cSMauro Carvalho Chehab 			     u16 Index, u16 Value, void *TransferBuffer,
19010aa77f6cSMauro Carvalho Chehab 			     s32 TransferBufferLength, int bOut)
19020aa77f6cSMauro Carvalho Chehab {
19030aa77f6cSMauro Carvalho Chehab 	int r;
1904*db65c49eSMauro Carvalho Chehab 	unsigned char *buf;
1905*db65c49eSMauro Carvalho Chehab 
1906*db65c49eSMauro Carvalho Chehab 	buf = kmalloc(TransferBufferLength, GFP_KERNEL);
1907*db65c49eSMauro Carvalho Chehab 	if (!buf)
1908*db65c49eSMauro Carvalho Chehab 		return -ENOMEM;
1909*db65c49eSMauro Carvalho Chehab 
19100aa77f6cSMauro Carvalho Chehab 	if (!bOut) {
19110aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
19120aa77f6cSMauro Carvalho Chehab 				    Request,
19130aa77f6cSMauro Carvalho Chehab 				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
19140aa77f6cSMauro Carvalho Chehab 				    USB_DIR_IN,
1915*db65c49eSMauro Carvalho Chehab 				    Value, Index, buf,
19160aa77f6cSMauro Carvalho Chehab 				    TransferBufferLength, HZ * 5);
1917*db65c49eSMauro Carvalho Chehab 
1918*db65c49eSMauro Carvalho Chehab 		if (r >= 0)
1919*db65c49eSMauro Carvalho Chehab 			memcpy(TransferBuffer, buf, TransferBufferLength);
19200aa77f6cSMauro Carvalho Chehab 	} else {
1921*db65c49eSMauro Carvalho Chehab 		memcpy(buf, TransferBuffer, TransferBufferLength);
19220aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
19230aa77f6cSMauro Carvalho Chehab 				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1924*db65c49eSMauro Carvalho Chehab 				    Value, Index, buf,
19250aa77f6cSMauro Carvalho Chehab 				    TransferBufferLength, HZ * 5);
19260aa77f6cSMauro Carvalho Chehab 	}
1927*db65c49eSMauro Carvalho Chehab 	kfree(buf);
19280aa77f6cSMauro Carvalho Chehab 	return r;
19290aa77f6cSMauro Carvalho Chehab }
19300aa77f6cSMauro Carvalho Chehab 
19310aa77f6cSMauro Carvalho Chehab /*
19320aa77f6cSMauro Carvalho Chehab  * retrieve FX2 firmware version. future use.
19330aa77f6cSMauro Carvalho Chehab  * @param dev pointer to device extension
19340aa77f6cSMauro Carvalho Chehab  * @return -1 for fail, else returns firmware version as an int(16 bits)
19350aa77f6cSMauro Carvalho Chehab  */
19360aa77f6cSMauro Carvalho Chehab static int s2255_get_fx2fw(struct s2255_dev *dev)
19370aa77f6cSMauro Carvalho Chehab {
19380aa77f6cSMauro Carvalho Chehab 	int fw;
19390aa77f6cSMauro Carvalho Chehab 	int ret;
19400aa77f6cSMauro Carvalho Chehab 	unsigned char transBuffer[64];
19410aa77f6cSMauro Carvalho Chehab 	ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
19420aa77f6cSMauro Carvalho Chehab 			       S2255_VR_IN);
19430aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
1944f5402007Ssensoray-dev 		dprintk(dev, 2, "get fw error: %x\n", ret);
19450aa77f6cSMauro Carvalho Chehab 	fw = transBuffer[0] + (transBuffer[1] << 8);
1946f5402007Ssensoray-dev 	dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
19470aa77f6cSMauro Carvalho Chehab 	return fw;
19480aa77f6cSMauro Carvalho Chehab }
19490aa77f6cSMauro Carvalho Chehab 
19500aa77f6cSMauro Carvalho Chehab /*
19510aa77f6cSMauro Carvalho Chehab  * Create the system ring buffer to copy frames into from the
19520aa77f6cSMauro Carvalho Chehab  * usb read pipe.
19530aa77f6cSMauro Carvalho Chehab  */
19545e950fafSDean Anderson static int s2255_create_sys_buffers(struct s2255_vc *vc)
19550aa77f6cSMauro Carvalho Chehab {
19560aa77f6cSMauro Carvalho Chehab 	unsigned long i;
19570aa77f6cSMauro Carvalho Chehab 	unsigned long reqsize;
19585e950fafSDean Anderson 	vc->buffer.dwFrames = SYS_FRAMES;
19590aa77f6cSMauro Carvalho Chehab 	/* always allocate maximum size(PAL) for system buffers */
19600aa77f6cSMauro Carvalho Chehab 	reqsize = SYS_FRAMES_MAXSIZE;
19610aa77f6cSMauro Carvalho Chehab 
19620aa77f6cSMauro Carvalho Chehab 	if (reqsize > SYS_FRAMES_MAXSIZE)
19630aa77f6cSMauro Carvalho Chehab 		reqsize = SYS_FRAMES_MAXSIZE;
19640aa77f6cSMauro Carvalho Chehab 
19650aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
19660aa77f6cSMauro Carvalho Chehab 		/* allocate the frames */
19675e950fafSDean Anderson 		vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
19685e950fafSDean Anderson 		vc->buffer.frame[i].size = reqsize;
19695e950fafSDean Anderson 		if (vc->buffer.frame[i].lpvbits == NULL) {
1970f5402007Ssensoray-dev 			pr_info("out of memory.  using less frames\n");
19715e950fafSDean Anderson 			vc->buffer.dwFrames = i;
19720aa77f6cSMauro Carvalho Chehab 			break;
19730aa77f6cSMauro Carvalho Chehab 		}
19740aa77f6cSMauro Carvalho Chehab 	}
19750aa77f6cSMauro Carvalho Chehab 
19760aa77f6cSMauro Carvalho Chehab 	/* make sure internal states are set */
19770aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
19785e950fafSDean Anderson 		vc->buffer.frame[i].ulState = 0;
19795e950fafSDean Anderson 		vc->buffer.frame[i].cur_size = 0;
19800aa77f6cSMauro Carvalho Chehab 	}
19810aa77f6cSMauro Carvalho Chehab 
19825e950fafSDean Anderson 	vc->cur_frame = 0;
19835e950fafSDean Anderson 	vc->last_frame = -1;
19840aa77f6cSMauro Carvalho Chehab 	return 0;
19850aa77f6cSMauro Carvalho Chehab }
19860aa77f6cSMauro Carvalho Chehab 
19875e950fafSDean Anderson static int s2255_release_sys_buffers(struct s2255_vc *vc)
19880aa77f6cSMauro Carvalho Chehab {
19890aa77f6cSMauro Carvalho Chehab 	unsigned long i;
19900aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
19915e950fafSDean Anderson 		vfree(vc->buffer.frame[i].lpvbits);
19925e950fafSDean Anderson 		vc->buffer.frame[i].lpvbits = NULL;
19930aa77f6cSMauro Carvalho Chehab 	}
19940aa77f6cSMauro Carvalho Chehab 	return 0;
19950aa77f6cSMauro Carvalho Chehab }
19960aa77f6cSMauro Carvalho Chehab 
19970aa77f6cSMauro Carvalho Chehab static int s2255_board_init(struct s2255_dev *dev)
19980aa77f6cSMauro Carvalho Chehab {
19990aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
20000aa77f6cSMauro Carvalho Chehab 	int fw_ver;
20010aa77f6cSMauro Carvalho Chehab 	int j;
20020aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
2003f5402007Ssensoray-dev 	dprintk(dev, 4, "board init: %p", dev);
20040aa77f6cSMauro Carvalho Chehab 	memset(pipe, 0, sizeof(*pipe));
20050aa77f6cSMauro Carvalho Chehab 	pipe->dev = dev;
20060aa77f6cSMauro Carvalho Chehab 	pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
20070aa77f6cSMauro Carvalho Chehab 	pipe->max_transfer_size = S2255_USB_XFER_SIZE;
20080aa77f6cSMauro Carvalho Chehab 
20090aa77f6cSMauro Carvalho Chehab 	pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
20100aa77f6cSMauro Carvalho Chehab 					GFP_KERNEL);
20110aa77f6cSMauro Carvalho Chehab 	if (pipe->transfer_buffer == NULL) {
2012f5402007Ssensoray-dev 		dprintk(dev, 1, "out of memory!\n");
20130aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
20140aa77f6cSMauro Carvalho Chehab 	}
20150aa77f6cSMauro Carvalho Chehab 	/* query the firmware */
20160aa77f6cSMauro Carvalho Chehab 	fw_ver = s2255_get_fx2fw(dev);
20170aa77f6cSMauro Carvalho Chehab 
2018f5402007Ssensoray-dev 	pr_info("s2255: usb firmware version %d.%d\n",
20190aa77f6cSMauro Carvalho Chehab 		(fw_ver >> 8) & 0xff,
20200aa77f6cSMauro Carvalho Chehab 		fw_ver & 0xff);
20210aa77f6cSMauro Carvalho Chehab 
20220aa77f6cSMauro Carvalho Chehab 	if (fw_ver < S2255_CUR_USB_FWVER)
2023f5402007Ssensoray-dev 		pr_info("s2255: newer USB firmware available\n");
20240aa77f6cSMauro Carvalho Chehab 
20250aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < MAX_CHANNELS; j++) {
20265e950fafSDean Anderson 		struct s2255_vc *vc = &dev->vc[j];
20275e950fafSDean Anderson 		vc->mode = mode_def;
20280aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 && j > 1)
20295e950fafSDean Anderson 			vc->mode.color |= (1 << 16);
20305e950fafSDean Anderson 		vc->jpegqual = S2255_DEF_JPEG_QUAL;
20315e950fafSDean Anderson 		vc->width = LINE_SZ_4CIFS_NTSC;
20325e950fafSDean Anderson 		vc->height = NUM_LINES_4CIFS_NTSC * 2;
20335e950fafSDean Anderson 		vc->std = V4L2_STD_NTSC_M;
20345e950fafSDean Anderson 		vc->fmt = &formats[0];
20355e950fafSDean Anderson 		vc->mode.restart = 1;
20365e950fafSDean Anderson 		vc->req_image_size = get_transfer_size(&mode_def);
20375e950fafSDean Anderson 		vc->frame_count = 0;
20380aa77f6cSMauro Carvalho Chehab 		/* create the system buffers */
20395e950fafSDean Anderson 		s2255_create_sys_buffers(vc);
20400aa77f6cSMauro Carvalho Chehab 	}
20410aa77f6cSMauro Carvalho Chehab 	/* start read pipe */
20420aa77f6cSMauro Carvalho Chehab 	s2255_start_readpipe(dev);
2043f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: success\n", __func__);
20440aa77f6cSMauro Carvalho Chehab 	return 0;
20450aa77f6cSMauro Carvalho Chehab }
20460aa77f6cSMauro Carvalho Chehab 
20470aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev)
20480aa77f6cSMauro Carvalho Chehab {
20490aa77f6cSMauro Carvalho Chehab 	u32 i;
2050f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: dev: %p", __func__,  dev);
20510aa77f6cSMauro Carvalho Chehab 
20520aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
2053340a30c5Ssensoray-dev 		if (vb2_is_streaming(&dev->vc[i].vb_vidq))
20545e950fafSDean Anderson 			s2255_stop_acquire(&dev->vc[i]);
20550aa77f6cSMauro Carvalho Chehab 	}
20560aa77f6cSMauro Carvalho Chehab 	s2255_stop_readpipe(dev);
20570aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++)
20585e950fafSDean Anderson 		s2255_release_sys_buffers(&dev->vc[i]);
20590aa77f6cSMauro Carvalho Chehab 	/* release transfer buffer */
20600aa77f6cSMauro Carvalho Chehab 	kfree(dev->pipe.transfer_buffer);
20610aa77f6cSMauro Carvalho Chehab 	return 0;
20620aa77f6cSMauro Carvalho Chehab }
20630aa77f6cSMauro Carvalho Chehab 
20640aa77f6cSMauro Carvalho Chehab static void read_pipe_completion(struct urb *purb)
20650aa77f6cSMauro Carvalho Chehab {
20660aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info;
20670aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev;
20680aa77f6cSMauro Carvalho Chehab 	int status;
20690aa77f6cSMauro Carvalho Chehab 	int pipe;
20700aa77f6cSMauro Carvalho Chehab 	pipe_info = purb->context;
20710aa77f6cSMauro Carvalho Chehab 	if (pipe_info == NULL) {
20720aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
20730aa77f6cSMauro Carvalho Chehab 		return;
20740aa77f6cSMauro Carvalho Chehab 	}
20750aa77f6cSMauro Carvalho Chehab 	dev = pipe_info->dev;
20760aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
20770aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
20780aa77f6cSMauro Carvalho Chehab 		return;
20790aa77f6cSMauro Carvalho Chehab 	}
20800aa77f6cSMauro Carvalho Chehab 	status = purb->status;
20810aa77f6cSMauro Carvalho Chehab 	/* if shutting down, do not resubmit, exit immediately */
20820aa77f6cSMauro Carvalho Chehab 	if (status == -ESHUTDOWN) {
2083f5402007Ssensoray-dev 		dprintk(dev, 2, "%s: err shutdown\n", __func__);
20840aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
20850aa77f6cSMauro Carvalho Chehab 		return;
20860aa77f6cSMauro Carvalho Chehab 	}
20870aa77f6cSMauro Carvalho Chehab 
20880aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state == 0) {
2089f5402007Ssensoray-dev 		dprintk(dev, 2, "%s: exiting USB pipe", __func__);
20900aa77f6cSMauro Carvalho Chehab 		return;
20910aa77f6cSMauro Carvalho Chehab 	}
20920aa77f6cSMauro Carvalho Chehab 
20930aa77f6cSMauro Carvalho Chehab 	if (status == 0)
20940aa77f6cSMauro Carvalho Chehab 		s2255_read_video_callback(dev, pipe_info);
20950aa77f6cSMauro Carvalho Chehab 	else {
20960aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
2097f5402007Ssensoray-dev 		dprintk(dev, 1, "%s: failed URB %d\n", __func__, status);
20980aa77f6cSMauro Carvalho Chehab 	}
20990aa77f6cSMauro Carvalho Chehab 
21000aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
21010aa77f6cSMauro Carvalho Chehab 	/* reuse urb */
21020aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
21030aa77f6cSMauro Carvalho Chehab 			  pipe,
21040aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
21050aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
21060aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
21070aa77f6cSMauro Carvalho Chehab 
21080aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state != 0) {
2109f5402007Ssensoray-dev 		if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC))
21100aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev, "error submitting urb\n");
21110aa77f6cSMauro Carvalho Chehab 	} else {
2112f5402007Ssensoray-dev 		dprintk(dev, 2, "%s :complete state 0\n", __func__);
21130aa77f6cSMauro Carvalho Chehab 	}
21140aa77f6cSMauro Carvalho Chehab 	return;
21150aa77f6cSMauro Carvalho Chehab }
21160aa77f6cSMauro Carvalho Chehab 
21170aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev)
21180aa77f6cSMauro Carvalho Chehab {
21190aa77f6cSMauro Carvalho Chehab 	int pipe;
21200aa77f6cSMauro Carvalho Chehab 	int retval;
21210aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info = &dev->pipe;
21220aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
2123f5402007Ssensoray-dev 	dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint);
21240aa77f6cSMauro Carvalho Chehab 	pipe_info->state = 1;
21250aa77f6cSMauro Carvalho Chehab 	pipe_info->err_count = 0;
21260aa77f6cSMauro Carvalho Chehab 	pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
2127fc56da79SWolfram Sang 	if (!pipe_info->stream_urb)
21280aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
21290aa77f6cSMauro Carvalho Chehab 	/* transfer buffer allocated in board_init */
21300aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
21310aa77f6cSMauro Carvalho Chehab 			  pipe,
21320aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
21330aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
21340aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
21350aa77f6cSMauro Carvalho Chehab 	retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
21360aa77f6cSMauro Carvalho Chehab 	if (retval) {
2137f5402007Ssensoray-dev 		pr_err("s2255: start read pipe failed\n");
21380aa77f6cSMauro Carvalho Chehab 		return retval;
21390aa77f6cSMauro Carvalho Chehab 	}
21400aa77f6cSMauro Carvalho Chehab 	return 0;
21410aa77f6cSMauro Carvalho Chehab }
21420aa77f6cSMauro Carvalho Chehab 
21430aa77f6cSMauro Carvalho Chehab /* starts acquisition process */
21445e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc)
21450aa77f6cSMauro Carvalho Chehab {
21460aa77f6cSMauro Carvalho Chehab 	int res;
21470aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
21480aa77f6cSMauro Carvalho Chehab 	int j;
21495e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
215047d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
21510aa77f6cSMauro Carvalho Chehab 
215247d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
215347d8c881SDean Anderson 	chn_rev = G_chnmap[vc->idx];
21545e950fafSDean Anderson 	vc->last_frame = -1;
21555e950fafSDean Anderson 	vc->bad_payload = 0;
21565e950fafSDean Anderson 	vc->cur_frame = 0;
21570aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
21585e950fafSDean Anderson 		vc->buffer.frame[j].ulState = 0;
21595e950fafSDean Anderson 		vc->buffer.frame[j].cur_size = 0;
21600aa77f6cSMauro Carvalho Chehab 	}
21610aa77f6cSMauro Carvalho Chehab 
21620aa77f6cSMauro Carvalho Chehab 	/* send the start command */
216347d8c881SDean Anderson 	buffer[0] = IN_DATA_TOKEN;
216447d8c881SDean Anderson 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
216547d8c881SDean Anderson 	buffer[2] = CMD_START;
21660aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
21670aa77f6cSMauro Carvalho Chehab 	if (res != 0)
21680aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_START error\n");
21690aa77f6cSMauro Carvalho Chehab 
21705e950fafSDean Anderson 	dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res);
217147d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
21726a5b63b3SDean Anderson 	return res;
21730aa77f6cSMauro Carvalho Chehab }
21740aa77f6cSMauro Carvalho Chehab 
21755e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc)
21760aa77f6cSMauro Carvalho Chehab {
21770aa77f6cSMauro Carvalho Chehab 	int res;
21780aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
21795e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
218047d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
218147d8c881SDean Anderson 
218247d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
21835e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
21840aa77f6cSMauro Carvalho Chehab 	/* send the stop command */
218547d8c881SDean Anderson 	buffer[0] = IN_DATA_TOKEN;
218647d8c881SDean Anderson 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
218747d8c881SDean Anderson 	buffer[2] = CMD_STOP;
218847d8c881SDean Anderson 
21890aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
21900aa77f6cSMauro Carvalho Chehab 	if (res != 0)
21910aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_STOP error\n");
219247d8c881SDean Anderson 
21935e950fafSDean Anderson 	dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res);
219447d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
21950aa77f6cSMauro Carvalho Chehab 	return res;
21960aa77f6cSMauro Carvalho Chehab }
21970aa77f6cSMauro Carvalho Chehab 
21980aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev)
21990aa77f6cSMauro Carvalho Chehab {
22000aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
22010aa77f6cSMauro Carvalho Chehab 
22020aa77f6cSMauro Carvalho Chehab 	pipe->state = 0;
22030aa77f6cSMauro Carvalho Chehab 	if (pipe->stream_urb) {
22040aa77f6cSMauro Carvalho Chehab 		/* cancel urb */
22050aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(pipe->stream_urb);
22060aa77f6cSMauro Carvalho Chehab 		usb_free_urb(pipe->stream_urb);
22070aa77f6cSMauro Carvalho Chehab 		pipe->stream_urb = NULL;
22080aa77f6cSMauro Carvalho Chehab 	}
2209f5402007Ssensoray-dev 	dprintk(dev, 4, "%s", __func__);
22100aa77f6cSMauro Carvalho Chehab 	return;
22110aa77f6cSMauro Carvalho Chehab }
22120aa77f6cSMauro Carvalho Chehab 
22130aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset)
22140aa77f6cSMauro Carvalho Chehab {
22150aa77f6cSMauro Carvalho Chehab 	if (reset)
22160aa77f6cSMauro Carvalho Chehab 		s2255_reset_dsppower(dev);
22170aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_size = dev->fw_data->fw->size;
22180aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
22190aa77f6cSMauro Carvalho Chehab 	memcpy(dev->fw_data->pfw_data,
22200aa77f6cSMauro Carvalho Chehab 	       dev->fw_data->fw->data, CHUNK_SIZE);
22210aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_loaded = CHUNK_SIZE;
22220aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
22230aa77f6cSMauro Carvalho Chehab 			  usb_sndbulkpipe(dev->udev, 2),
22240aa77f6cSMauro Carvalho Chehab 			  dev->fw_data->pfw_data,
22250aa77f6cSMauro Carvalho Chehab 			  CHUNK_SIZE, s2255_fwchunk_complete,
22260aa77f6cSMauro Carvalho Chehab 			  dev->fw_data);
22270aa77f6cSMauro Carvalho Chehab 	mod_timer(&dev->timer, jiffies + HZ);
22280aa77f6cSMauro Carvalho Chehab }
22290aa77f6cSMauro Carvalho Chehab 
22300aa77f6cSMauro Carvalho Chehab /* standard usb probe function */
22310aa77f6cSMauro Carvalho Chehab static int s2255_probe(struct usb_interface *interface,
22320aa77f6cSMauro Carvalho Chehab 		       const struct usb_device_id *id)
22330aa77f6cSMauro Carvalho Chehab {
22340aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = NULL;
22350aa77f6cSMauro Carvalho Chehab 	struct usb_host_interface *iface_desc;
22360aa77f6cSMauro Carvalho Chehab 	struct usb_endpoint_descriptor *endpoint;
22370aa77f6cSMauro Carvalho Chehab 	int i;
22380aa77f6cSMauro Carvalho Chehab 	int retval = -ENOMEM;
22390aa77f6cSMauro Carvalho Chehab 	__le32 *pdata;
22400aa77f6cSMauro Carvalho Chehab 	int fw_size;
224147d8c881SDean Anderson 
22420aa77f6cSMauro Carvalho Chehab 	/* allocate memory for our device state and initialize it to zero */
22430aa77f6cSMauro Carvalho Chehab 	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
22440aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
22450aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&interface->dev, "out of memory\n");
22460aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
22470aa77f6cSMauro Carvalho Chehab 	}
224847d8c881SDean Anderson 
224947d8c881SDean Anderson 	dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
225047d8c881SDean Anderson 	if (dev->cmdbuf == NULL) {
225147d8c881SDean Anderson 		s2255_dev_err(&interface->dev, "out of memory\n");
2252e21c94e7SDaeseok Youn 		goto errorFWDATA1;
225347d8c881SDean Anderson 	}
225447d8c881SDean Anderson 
22550aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->num_channels, 0);
2256ff3ec57dSHans Verkuil 	dev->pid = id->idProduct;
22570aa77f6cSMauro Carvalho Chehab 	dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
22580aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data)
22590aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA1;
22600aa77f6cSMauro Carvalho Chehab 	mutex_init(&dev->lock);
226147d8c881SDean Anderson 	mutex_init(&dev->cmdlock);
22620aa77f6cSMauro Carvalho Chehab 	/* grab usb_device and save it */
22630aa77f6cSMauro Carvalho Chehab 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
22640aa77f6cSMauro Carvalho Chehab 	if (dev->udev == NULL) {
22650aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "null usb device\n");
22660aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
22670aa77f6cSMauro Carvalho Chehab 		goto errorUDEV;
22680aa77f6cSMauro Carvalho Chehab 	}
2269f5402007Ssensoray-dev 	dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
2270f5402007Ssensoray-dev 		dev, dev->udev, interface);
22710aa77f6cSMauro Carvalho Chehab 	dev->interface = interface;
22720aa77f6cSMauro Carvalho Chehab 	/* set up the endpoint information  */
22730aa77f6cSMauro Carvalho Chehab 	iface_desc = interface->cur_altsetting;
2274f5402007Ssensoray-dev 	dev_dbg(&interface->dev, "num EP: %d\n",
2275f5402007Ssensoray-dev 		iface_desc->desc.bNumEndpoints);
22760aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
22770aa77f6cSMauro Carvalho Chehab 		endpoint = &iface_desc->endpoint[i].desc;
22780aa77f6cSMauro Carvalho Chehab 		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
22790aa77f6cSMauro Carvalho Chehab 			/* we found the bulk in endpoint */
22800aa77f6cSMauro Carvalho Chehab 			dev->read_endpoint = endpoint->bEndpointAddress;
22810aa77f6cSMauro Carvalho Chehab 		}
22820aa77f6cSMauro Carvalho Chehab 	}
22830aa77f6cSMauro Carvalho Chehab 
22840aa77f6cSMauro Carvalho Chehab 	if (!dev->read_endpoint) {
22850aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
22860aa77f6cSMauro Carvalho Chehab 		goto errorEP;
22870aa77f6cSMauro Carvalho Chehab 	}
22888bf554caSJulia Lawall 	setup_timer(&dev->timer, s2255_timer, (unsigned long)dev->fw_data);
22890aa77f6cSMauro Carvalho Chehab 	init_waitqueue_head(&dev->fw_data->wait_fw);
22900aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
22915e950fafSDean Anderson 		struct s2255_vc *vc = &dev->vc[i];
22925e950fafSDean Anderson 		vc->idx = i;
22935e950fafSDean Anderson 		vc->dev = dev;
22945e950fafSDean Anderson 		init_waitqueue_head(&vc->wait_setmode);
22955e950fafSDean Anderson 		init_waitqueue_head(&vc->wait_vidstatus);
2296340a30c5Ssensoray-dev 		spin_lock_init(&vc->qlock);
2297340a30c5Ssensoray-dev 		mutex_init(&vc->vb_lock);
22980aa77f6cSMauro Carvalho Chehab 	}
22990aa77f6cSMauro Carvalho Chehab 
23000aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
2301fc56da79SWolfram Sang 	if (!dev->fw_data->fw_urb)
23020aa77f6cSMauro Carvalho Chehab 		goto errorFWURB;
23030aa77f6cSMauro Carvalho Chehab 
23040aa77f6cSMauro Carvalho Chehab 	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
23050aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data->pfw_data) {
23060aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "out of memory!\n");
23070aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA2;
23080aa77f6cSMauro Carvalho Chehab 	}
23090aa77f6cSMauro Carvalho Chehab 	/* load the first chunk */
23100aa77f6cSMauro Carvalho Chehab 	if (request_firmware(&dev->fw_data->fw,
23110aa77f6cSMauro Carvalho Chehab 			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
2312f5402007Ssensoray-dev 		dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n");
23130aa77f6cSMauro Carvalho Chehab 		goto errorREQFW;
23140aa77f6cSMauro Carvalho Chehab 	}
23150aa77f6cSMauro Carvalho Chehab 	/* check the firmware is valid */
23160aa77f6cSMauro Carvalho Chehab 	fw_size = dev->fw_data->fw->size;
23170aa77f6cSMauro Carvalho Chehab 	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
23180aa77f6cSMauro Carvalho Chehab 
23190aa77f6cSMauro Carvalho Chehab 	if (*pdata != S2255_FW_MARKER) {
2320f5402007Ssensoray-dev 		dev_err(&interface->dev, "Firmware invalid.\n");
23210aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
23220aa77f6cSMauro Carvalho Chehab 		goto errorFWMARKER;
23230aa77f6cSMauro Carvalho Chehab 	} else {
23240aa77f6cSMauro Carvalho Chehab 		/* make sure firmware is the latest */
23250aa77f6cSMauro Carvalho Chehab 		__le32 *pRel;
23260aa77f6cSMauro Carvalho Chehab 		pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
2327f5402007Ssensoray-dev 		pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
23280aa77f6cSMauro Carvalho Chehab 		dev->dsp_fw_ver = le32_to_cpu(*pRel);
23290aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
2330f5402007Ssensoray-dev 			pr_info("s2255: f2255usb.bin out of date.\n");
23310aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 &&
23320aa77f6cSMauro Carvalho Chehab 				dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
2333f5402007Ssensoray-dev 			pr_warn("2257 needs firmware %d or above.\n",
2334f5402007Ssensoray-dev 				S2255_MIN_DSP_COLORFILTER);
23350aa77f6cSMauro Carvalho Chehab 	}
23360aa77f6cSMauro Carvalho Chehab 	usb_reset_device(dev->udev);
23370aa77f6cSMauro Carvalho Chehab 	/* load 2255 board specific */
23380aa77f6cSMauro Carvalho Chehab 	retval = s2255_board_init(dev);
23390aa77f6cSMauro Carvalho Chehab 	if (retval)
23400aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
23410aa77f6cSMauro Carvalho Chehab 	s2255_fwload_start(dev, 0);
23420aa77f6cSMauro Carvalho Chehab 	/* loads v4l specific */
23430aa77f6cSMauro Carvalho Chehab 	retval = s2255_probe_v4l(dev);
23440aa77f6cSMauro Carvalho Chehab 	if (retval)
23450aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
23460aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "Sensoray 2255 detected\n");
23470aa77f6cSMauro Carvalho Chehab 	return 0;
23480aa77f6cSMauro Carvalho Chehab errorBOARDINIT:
23490aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
23500aa77f6cSMauro Carvalho Chehab errorFWMARKER:
23510aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
23520aa77f6cSMauro Carvalho Chehab errorREQFW:
23530aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
23540aa77f6cSMauro Carvalho Chehab errorFWDATA2:
23550aa77f6cSMauro Carvalho Chehab 	usb_free_urb(dev->fw_data->fw_urb);
23560aa77f6cSMauro Carvalho Chehab errorFWURB:
23579f6be2bcSKirill Tkhai 	del_timer_sync(&dev->timer);
23580aa77f6cSMauro Carvalho Chehab errorEP:
23590aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
23600aa77f6cSMauro Carvalho Chehab errorUDEV:
23610aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
23620aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
23630aa77f6cSMauro Carvalho Chehab errorFWDATA1:
236447d8c881SDean Anderson 	kfree(dev->cmdbuf);
23650aa77f6cSMauro Carvalho Chehab 	kfree(dev);
2366f5402007Ssensoray-dev 	pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval);
23670aa77f6cSMauro Carvalho Chehab 	return retval;
23680aa77f6cSMauro Carvalho Chehab }
23690aa77f6cSMauro Carvalho Chehab 
23700aa77f6cSMauro Carvalho Chehab /* disconnect routine. when board is removed physically or with rmmod */
23710aa77f6cSMauro Carvalho Chehab static void s2255_disconnect(struct usb_interface *interface)
23720aa77f6cSMauro Carvalho Chehab {
23730aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
23740aa77f6cSMauro Carvalho Chehab 	int i;
23750aa77f6cSMauro Carvalho Chehab 	int channels = atomic_read(&dev->num_channels);
23760aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
23770aa77f6cSMauro Carvalho Chehab 	v4l2_device_disconnect(&dev->v4l2_dev);
23780aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
23790aa77f6cSMauro Carvalho Chehab 	/*see comments in the uvc_driver.c usb disconnect function */
23800aa77f6cSMauro Carvalho Chehab 	atomic_inc(&dev->num_channels);
23810aa77f6cSMauro Carvalho Chehab 	/* unregister each video device. */
23820aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < channels; i++)
23835e950fafSDean Anderson 		video_unregister_device(&dev->vc[i].vdev);
23840aa77f6cSMauro Carvalho Chehab 	/* wake up any of our timers */
23850aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
23860aa77f6cSMauro Carvalho Chehab 	wake_up(&dev->fw_data->wait_fw);
23870aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
23885e950fafSDean Anderson 		dev->vc[i].setmode_ready = 1;
23895e950fafSDean Anderson 		wake_up(&dev->vc[i].wait_setmode);
23905e950fafSDean Anderson 		dev->vc[i].vidstatus_ready = 1;
23915e950fafSDean Anderson 		wake_up(&dev->vc[i].wait_vidstatus);
23920aa77f6cSMauro Carvalho Chehab 	}
23930aa77f6cSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->num_channels))
23940aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
23950aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "%s\n", __func__);
23960aa77f6cSMauro Carvalho Chehab }
23970aa77f6cSMauro Carvalho Chehab 
23980aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver = {
23990aa77f6cSMauro Carvalho Chehab 	.name = S2255_DRIVER_NAME,
24000aa77f6cSMauro Carvalho Chehab 	.probe = s2255_probe,
24010aa77f6cSMauro Carvalho Chehab 	.disconnect = s2255_disconnect,
24020aa77f6cSMauro Carvalho Chehab 	.id_table = s2255_table,
24030aa77f6cSMauro Carvalho Chehab };
24040aa77f6cSMauro Carvalho Chehab 
24050aa77f6cSMauro Carvalho Chehab module_usb_driver(s2255_driver);
24060aa77f6cSMauro Carvalho Chehab 
24070aa77f6cSMauro Carvalho Chehab MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
24080aa77f6cSMauro Carvalho Chehab MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
24090aa77f6cSMauro Carvalho Chehab MODULE_LICENSE("GPL");
24100aa77f6cSMauro Carvalho Chehab MODULE_VERSION(S2255_VERSION);
24110aa77f6cSMauro Carvalho Chehab MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
2412