xref: /openbmc/linux/drivers/media/usb/s2255/s2255drv.c (revision ee1cd5048959de496cd005c50b137212a5b62062)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20aa77f6cSMauro Carvalho Chehab /*
30aa77f6cSMauro Carvalho Chehab  *  s2255drv.c - a driver for the Sensoray 2255 USB video capture device
40aa77f6cSMauro Carvalho Chehab  *
5d86c6a8cSDean Anderson  *   Copyright (C) 2007-2014 by Sensoray Company Inc.
60aa77f6cSMauro Carvalho Chehab  *                              Dean Anderson
70aa77f6cSMauro Carvalho Chehab  *
80aa77f6cSMauro Carvalho Chehab  * Some video buffer code based on vivi driver:
90aa77f6cSMauro Carvalho Chehab  *
100aa77f6cSMauro Carvalho Chehab  * Sensoray 2255 device supports 4 simultaneous channels.
110aa77f6cSMauro Carvalho Chehab  * The channels are not "crossbar" inputs, they are physically
120aa77f6cSMauro Carvalho Chehab  * attached to separate video decoders.
130aa77f6cSMauro Carvalho Chehab  *
140aa77f6cSMauro Carvalho Chehab  * Because of USB2.0 bandwidth limitations. There is only a
150aa77f6cSMauro Carvalho Chehab  * certain amount of data which may be transferred at one time.
160aa77f6cSMauro Carvalho Chehab  *
170aa77f6cSMauro Carvalho Chehab  * Example maximum bandwidth utilization:
180aa77f6cSMauro Carvalho Chehab  *
190aa77f6cSMauro Carvalho Chehab  * -full size, color mode YUYV or YUV422P: 2 channels at once
200aa77f6cSMauro Carvalho Chehab  * -full or half size Grey scale: all 4 channels at once
210aa77f6cSMauro Carvalho Chehab  * -half size, color mode YUYV or YUV422P: all 4 channels at once
220aa77f6cSMauro Carvalho Chehab  * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
230aa77f6cSMauro Carvalho Chehab  *  at once.
240aa77f6cSMauro Carvalho Chehab  */
250aa77f6cSMauro Carvalho Chehab 
260aa77f6cSMauro Carvalho Chehab #include <linux/module.h>
270aa77f6cSMauro Carvalho Chehab #include <linux/firmware.h>
280aa77f6cSMauro Carvalho Chehab #include <linux/kernel.h>
290aa77f6cSMauro Carvalho Chehab #include <linux/mutex.h>
300aa77f6cSMauro Carvalho Chehab #include <linux/slab.h>
310aa77f6cSMauro Carvalho Chehab #include <linux/videodev2.h>
320aa77f6cSMauro Carvalho Chehab #include <linux/mm.h>
3344d06d82SHans Verkuil #include <linux/vmalloc.h>
3444d06d82SHans Verkuil #include <linux/usb.h>
352d700715SJunghak Sung #include <media/videobuf2-v4l2.h>
36340a30c5Ssensoray-dev #include <media/videobuf2-vmalloc.h>
370aa77f6cSMauro Carvalho Chehab #include <media/v4l2-common.h>
380aa77f6cSMauro Carvalho Chehab #include <media/v4l2-device.h>
390aa77f6cSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
40192f1e78SHans Verkuil #include <media/v4l2-ctrls.h>
4144d06d82SHans Verkuil #include <media/v4l2-event.h>
420aa77f6cSMauro Carvalho Chehab 
43340a30c5Ssensoray-dev #define S2255_VERSION		"1.25.1"
440aa77f6cSMauro Carvalho Chehab #define FIRMWARE_FILE_NAME "f2255usb.bin"
450aa77f6cSMauro Carvalho Chehab 
460aa77f6cSMauro Carvalho Chehab /* default JPEG quality */
470aa77f6cSMauro Carvalho Chehab #define S2255_DEF_JPEG_QUAL     50
480aa77f6cSMauro Carvalho Chehab /* vendor request in */
490aa77f6cSMauro Carvalho Chehab #define S2255_VR_IN		0
500aa77f6cSMauro Carvalho Chehab /* vendor request out */
510aa77f6cSMauro Carvalho Chehab #define S2255_VR_OUT		1
520aa77f6cSMauro Carvalho Chehab /* firmware query */
530aa77f6cSMauro Carvalho Chehab #define S2255_VR_FW		0x30
540aa77f6cSMauro Carvalho Chehab /* USB endpoint number for configuring the device */
550aa77f6cSMauro Carvalho Chehab #define S2255_CONFIG_EP         2
560aa77f6cSMauro Carvalho Chehab /* maximum time for DSP to start responding after last FW word loaded(ms) */
570aa77f6cSMauro Carvalho Chehab #define S2255_DSP_BOOTTIME      800
580aa77f6cSMauro Carvalho Chehab /* maximum time to wait for firmware to load (ms) */
590aa77f6cSMauro Carvalho Chehab #define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
609da62eb0SDean Anderson #define S2255_MIN_BUFS          2
610aa77f6cSMauro Carvalho Chehab #define S2255_SETMODE_TIMEOUT   500
620aa77f6cSMauro Carvalho Chehab #define S2255_VIDSTATUS_TIMEOUT 350
630aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_FRAME	cpu_to_le32(0x2255DA4AL)
640aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_RESPONSE	cpu_to_le32(0x2255ACACL)
650aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_SETMODE  cpu_to_le32(0x01)
660aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_FW       cpu_to_le32(0x10)
670aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_STATUS   cpu_to_le32(0x20)
680aa77f6cSMauro Carvalho Chehab #define S2255_USB_XFER_SIZE	(16 * 1024)
690aa77f6cSMauro Carvalho Chehab #define MAX_CHANNELS		4
700aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES		4
710aa77f6cSMauro Carvalho Chehab /* maximum size is PAL full size plus room for the marker header(s) */
720aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
730aa77f6cSMauro Carvalho Chehab #define DEF_USB_BLOCK		S2255_USB_XFER_SIZE
740aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_NTSC	640
750aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_NTSC	640
760aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_NTSC	320
770aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_PAL	704
780aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_PAL	704
790aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_PAL	352
800aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_NTSC	240
810aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_NTSC	240
820aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_NTSC	240
830aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_PAL	288
840aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_PAL	288
850aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_PAL	288
860aa77f6cSMauro Carvalho Chehab #define LINE_SZ_DEF		640
870aa77f6cSMauro Carvalho Chehab #define NUM_LINES_DEF		240
880aa77f6cSMauro Carvalho Chehab 
890aa77f6cSMauro Carvalho Chehab 
900aa77f6cSMauro Carvalho Chehab /* predefined settings */
910aa77f6cSMauro Carvalho Chehab #define FORMAT_NTSC	1
920aa77f6cSMauro Carvalho Chehab #define FORMAT_PAL	2
930aa77f6cSMauro Carvalho Chehab 
940aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
950aa77f6cSMauro Carvalho Chehab #define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
960aa77f6cSMauro Carvalho Chehab #define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
970aa77f6cSMauro Carvalho Chehab /* SCALE_4CIFSI is the 2 fields interpolated into one */
980aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFSI	4	/* 640x480(NTSC) or 704x576(PAL) high quality */
990aa77f6cSMauro Carvalho Chehab 
1000aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPL	1	/* YUV planar */
1010aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPK	2	/* YUV packed */
1020aa77f6cSMauro Carvalho Chehab #define COLOR_Y8	4	/* monochrome */
1030aa77f6cSMauro Carvalho Chehab #define COLOR_JPG       5       /* JPEG */
1040aa77f6cSMauro Carvalho Chehab 
1050aa77f6cSMauro Carvalho Chehab #define MASK_COLOR       0x000000ff
1060aa77f6cSMauro Carvalho Chehab #define MASK_JPG_QUALITY 0x0000ff00
1070aa77f6cSMauro Carvalho Chehab #define MASK_INPUT_TYPE  0x000f0000
1080aa77f6cSMauro Carvalho Chehab /* frame decimation. */
1090aa77f6cSMauro Carvalho Chehab #define FDEC_1		1	/* capture every frame. default */
1100aa77f6cSMauro Carvalho Chehab #define FDEC_2		2	/* capture every 2nd frame */
1110aa77f6cSMauro Carvalho Chehab #define FDEC_3		3	/* capture every 3rd frame */
1120aa77f6cSMauro Carvalho Chehab #define FDEC_5		5	/* capture every 5th frame */
1130aa77f6cSMauro Carvalho Chehab 
1140aa77f6cSMauro Carvalho Chehab /*-------------------------------------------------------
1150aa77f6cSMauro Carvalho Chehab  * Default mode parameters.
1160aa77f6cSMauro Carvalho Chehab  *-------------------------------------------------------*/
1170aa77f6cSMauro Carvalho Chehab #define DEF_SCALE	SCALE_4CIFS
1180aa77f6cSMauro Carvalho Chehab #define DEF_COLOR	COLOR_YUVPL
1190aa77f6cSMauro Carvalho Chehab #define DEF_FDEC	FDEC_1
1200aa77f6cSMauro Carvalho Chehab #define DEF_BRIGHT	0
1210aa77f6cSMauro Carvalho Chehab #define DEF_CONTRAST	0x5c
1220aa77f6cSMauro Carvalho Chehab #define DEF_SATURATION	0x80
1230aa77f6cSMauro Carvalho Chehab #define DEF_HUE		0
1240aa77f6cSMauro Carvalho Chehab 
1250aa77f6cSMauro Carvalho Chehab /* usb config commands */
1260aa77f6cSMauro Carvalho Chehab #define IN_DATA_TOKEN	cpu_to_le32(0x2255c0de)
1270aa77f6cSMauro Carvalho Chehab #define CMD_2255	0xc2255000
1280aa77f6cSMauro Carvalho Chehab #define CMD_SET_MODE	cpu_to_le32((CMD_2255 | 0x10))
1290aa77f6cSMauro Carvalho Chehab #define CMD_START	cpu_to_le32((CMD_2255 | 0x20))
1300aa77f6cSMauro Carvalho Chehab #define CMD_STOP	cpu_to_le32((CMD_2255 | 0x30))
1310aa77f6cSMauro Carvalho Chehab #define CMD_STATUS	cpu_to_le32((CMD_2255 | 0x40))
1320aa77f6cSMauro Carvalho Chehab 
1330aa77f6cSMauro Carvalho Chehab struct s2255_mode {
1340aa77f6cSMauro Carvalho Chehab 	u32 format;	/* input video format (NTSC, PAL) */
1350aa77f6cSMauro Carvalho Chehab 	u32 scale;	/* output video scale */
1360aa77f6cSMauro Carvalho Chehab 	u32 color;	/* output video color format */
1370aa77f6cSMauro Carvalho Chehab 	u32 fdec;	/* frame decimation */
1380aa77f6cSMauro Carvalho Chehab 	u32 bright;	/* brightness */
1390aa77f6cSMauro Carvalho Chehab 	u32 contrast;	/* contrast */
1400aa77f6cSMauro Carvalho Chehab 	u32 saturation;	/* saturation */
1410aa77f6cSMauro Carvalho Chehab 	u32 hue;	/* hue (NTSC only)*/
1420aa77f6cSMauro Carvalho Chehab 	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
1430aa77f6cSMauro Carvalho Chehab 	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
1440aa77f6cSMauro Carvalho Chehab 	u32 restart;	/* if DSP requires restart */
1450aa77f6cSMauro Carvalho Chehab };
1460aa77f6cSMauro Carvalho Chehab 
1470aa77f6cSMauro Carvalho Chehab 
1480aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE		0
1490aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME	1
1500aa77f6cSMauro Carvalho Chehab 
1510aa77f6cSMauro Carvalho Chehab /* frame structure */
1520aa77f6cSMauro Carvalho Chehab struct s2255_framei {
1530aa77f6cSMauro Carvalho Chehab 	unsigned long size;
1540aa77f6cSMauro Carvalho Chehab 	unsigned long ulState;	/* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
1550aa77f6cSMauro Carvalho Chehab 	void *lpvbits;		/* image data */
1560aa77f6cSMauro Carvalho Chehab 	unsigned long cur_size;	/* current data copied to it */
1570aa77f6cSMauro Carvalho Chehab };
1580aa77f6cSMauro Carvalho Chehab 
1590aa77f6cSMauro Carvalho Chehab /* image buffer structure */
1600aa77f6cSMauro Carvalho Chehab struct s2255_bufferi {
1610aa77f6cSMauro Carvalho Chehab 	unsigned long dwFrames;			/* number of frames in buffer */
1620aa77f6cSMauro Carvalho Chehab 	struct s2255_framei frame[SYS_FRAMES];	/* array of FRAME structures */
1630aa77f6cSMauro Carvalho Chehab };
1640aa77f6cSMauro Carvalho Chehab 
1650aa77f6cSMauro Carvalho Chehab #define DEF_MODEI_NTSC_CONT	{FORMAT_NTSC, DEF_SCALE, DEF_COLOR,	\
1660aa77f6cSMauro Carvalho Chehab 			DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
1670aa77f6cSMauro Carvalho Chehab 			DEF_HUE, 0, DEF_USB_BLOCK, 0}
1680aa77f6cSMauro Carvalho Chehab 
1690aa77f6cSMauro Carvalho Chehab /* for firmware loading, fw_state */
1700aa77f6cSMauro Carvalho Chehab #define S2255_FW_NOTLOADED	0
1710aa77f6cSMauro Carvalho Chehab #define S2255_FW_LOADED_DSPWAIT	1
1720aa77f6cSMauro Carvalho Chehab #define S2255_FW_SUCCESS	2
1730aa77f6cSMauro Carvalho Chehab #define S2255_FW_FAILED		3
1740aa77f6cSMauro Carvalho Chehab #define S2255_FW_DISCONNECTING  4
1750aa77f6cSMauro Carvalho Chehab #define S2255_FW_MARKER		cpu_to_le32(0x22552f2f)
1760aa77f6cSMauro Carvalho Chehab /* 2255 read states */
1770aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE         0
1780aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME        1
1790aa77f6cSMauro Carvalho Chehab struct s2255_fw {
1800aa77f6cSMauro Carvalho Chehab 	int		      fw_loaded;
1810aa77f6cSMauro Carvalho Chehab 	int		      fw_size;
1820aa77f6cSMauro Carvalho Chehab 	struct urb	      *fw_urb;
1830aa77f6cSMauro Carvalho Chehab 	atomic_t	      fw_state;
1840aa77f6cSMauro Carvalho Chehab 	void		      *pfw_data;
1850aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t     wait_fw;
1860aa77f6cSMauro Carvalho Chehab 	const struct firmware *fw;
1870aa77f6cSMauro Carvalho Chehab };
1880aa77f6cSMauro Carvalho Chehab 
1890aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo {
1900aa77f6cSMauro Carvalho Chehab 	u32 max_transfer_size;
1910aa77f6cSMauro Carvalho Chehab 	u32 cur_transfer_size;
1920aa77f6cSMauro Carvalho Chehab 	u8 *transfer_buffer;
1930aa77f6cSMauro Carvalho Chehab 	u32 state;
1940aa77f6cSMauro Carvalho Chehab 	void *stream_urb;
1950aa77f6cSMauro Carvalho Chehab 	void *dev;	/* back pointer to s2255_dev struct*/
1960aa77f6cSMauro Carvalho Chehab 	u32 err_count;
1970aa77f6cSMauro Carvalho Chehab 	u32 idx;
1980aa77f6cSMauro Carvalho Chehab };
1990aa77f6cSMauro Carvalho Chehab 
2000aa77f6cSMauro Carvalho Chehab struct s2255_fmt; /*forward declaration */
2010aa77f6cSMauro Carvalho Chehab struct s2255_dev;
2020aa77f6cSMauro Carvalho Chehab 
2035e950fafSDean Anderson /* 2255 video channel */
2045e950fafSDean Anderson struct s2255_vc {
205f5402007Ssensoray-dev 	struct s2255_dev        *dev;
2060aa77f6cSMauro Carvalho Chehab 	struct video_device	vdev;
207192f1e78SHans Verkuil 	struct v4l2_ctrl_handler hdl;
2087041dec7SHans Verkuil 	struct v4l2_ctrl	*jpegqual_ctrl;
2090aa77f6cSMauro Carvalho Chehab 	int			resources;
210d86c6a8cSDean Anderson 	struct list_head        buf_list;
2110aa77f6cSMauro Carvalho Chehab 	struct s2255_bufferi	buffer;
2120aa77f6cSMauro Carvalho Chehab 	struct s2255_mode	mode;
213469af77aSHans Verkuil 	v4l2_std_id		std;
2140aa77f6cSMauro Carvalho Chehab 	/* jpeg compression */
2157041dec7SHans Verkuil 	unsigned		jpegqual;
2160aa77f6cSMauro Carvalho Chehab 	/* capture parameters (for high quality mode full size) */
2170aa77f6cSMauro Carvalho Chehab 	struct v4l2_captureparm cap_parm;
2180aa77f6cSMauro Carvalho Chehab 	int			cur_frame;
2190aa77f6cSMauro Carvalho Chehab 	int			last_frame;
2200aa77f6cSMauro Carvalho Chehab 	/* allocated image size */
2210aa77f6cSMauro Carvalho Chehab 	unsigned long		req_image_size;
2220aa77f6cSMauro Carvalho Chehab 	/* received packet size */
2230aa77f6cSMauro Carvalho Chehab 	unsigned long		pkt_size;
2240aa77f6cSMauro Carvalho Chehab 	int			bad_payload;
2250aa77f6cSMauro Carvalho Chehab 	unsigned long		frame_count;
2260aa77f6cSMauro Carvalho Chehab 	/* if JPEG image */
2270aa77f6cSMauro Carvalho Chehab 	int                     jpg_size;
2280aa77f6cSMauro Carvalho Chehab 	/* if channel configured to default state */
2290aa77f6cSMauro Carvalho Chehab 	int                     configured;
2300aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_setmode;
2310aa77f6cSMauro Carvalho Chehab 	int                     setmode_ready;
2320aa77f6cSMauro Carvalho Chehab 	/* video status items */
2330aa77f6cSMauro Carvalho Chehab 	int                     vidstatus;
2340aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_vidstatus;
2350aa77f6cSMauro Carvalho Chehab 	int                     vidstatus_ready;
2360aa77f6cSMauro Carvalho Chehab 	unsigned int		width;
2370aa77f6cSMauro Carvalho Chehab 	unsigned int		height;
238340a30c5Ssensoray-dev 	enum v4l2_field         field;
2390aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt	*fmt;
2400aa77f6cSMauro Carvalho Chehab 	int idx; /* channel number on device, 0-3 */
241340a30c5Ssensoray-dev 	struct vb2_queue vb_vidq;
242340a30c5Ssensoray-dev 	struct mutex vb_lock; /* streaming lock */
243340a30c5Ssensoray-dev 	spinlock_t qlock;
2440aa77f6cSMauro Carvalho Chehab };
2450aa77f6cSMauro Carvalho Chehab 
2460aa77f6cSMauro Carvalho Chehab 
2470aa77f6cSMauro Carvalho Chehab struct s2255_dev {
2485e950fafSDean Anderson 	struct s2255_vc         vc[MAX_CHANNELS];
2490aa77f6cSMauro Carvalho Chehab 	struct v4l2_device      v4l2_dev;
250*29ce81f9SRicardo Ribalda 	refcount_t		num_channels;
2510aa77f6cSMauro Carvalho Chehab 	int			frames;
2520aa77f6cSMauro Carvalho Chehab 	struct mutex		lock;	/* channels[].vdev.lock */
25347d8c881SDean Anderson 	struct mutex		cmdlock; /* protects cmdbuf */
2540aa77f6cSMauro Carvalho Chehab 	struct usb_device	*udev;
2550aa77f6cSMauro Carvalho Chehab 	struct usb_interface	*interface;
2560aa77f6cSMauro Carvalho Chehab 	u8			read_endpoint;
2570aa77f6cSMauro Carvalho Chehab 	struct timer_list	timer;
2580aa77f6cSMauro Carvalho Chehab 	struct s2255_fw	*fw_data;
2590aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo	pipe;
2600aa77f6cSMauro Carvalho Chehab 	u32			cc;	/* current channel */
2610aa77f6cSMauro Carvalho Chehab 	int			frame_ready;
2620aa77f6cSMauro Carvalho Chehab 	int                     chn_ready;
2630aa77f6cSMauro Carvalho Chehab 	/* dsp firmware version (f2255usb.bin) */
2640aa77f6cSMauro Carvalho Chehab 	int                     dsp_fw_ver;
2650aa77f6cSMauro Carvalho Chehab 	u16                     pid; /* product id */
26647d8c881SDean Anderson #define S2255_CMDBUF_SIZE 512
26747d8c881SDean Anderson 	__le32                  *cmdbuf;
2680aa77f6cSMauro Carvalho Chehab };
2690aa77f6cSMauro Carvalho Chehab 
to_s2255_dev(struct v4l2_device * v4l2_dev)2700aa77f6cSMauro Carvalho Chehab static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
2710aa77f6cSMauro Carvalho Chehab {
2720aa77f6cSMauro Carvalho Chehab 	return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
2730aa77f6cSMauro Carvalho Chehab }
2740aa77f6cSMauro Carvalho Chehab 
2750aa77f6cSMauro Carvalho Chehab struct s2255_fmt {
2760aa77f6cSMauro Carvalho Chehab 	u32 fourcc;
2770aa77f6cSMauro Carvalho Chehab 	int depth;
2780aa77f6cSMauro Carvalho Chehab };
2790aa77f6cSMauro Carvalho Chehab 
2800aa77f6cSMauro Carvalho Chehab /* buffer for one video frame */
2810aa77f6cSMauro Carvalho Chehab struct s2255_buffer {
2820aa77f6cSMauro Carvalho Chehab 	/* common v4l buffer stuff -- must be first */
2832d700715SJunghak Sung 	struct vb2_v4l2_buffer vb;
284340a30c5Ssensoray-dev 	struct list_head list;
2850aa77f6cSMauro Carvalho Chehab };
2860aa77f6cSMauro Carvalho Chehab 
2870aa77f6cSMauro Carvalho Chehab 
2880aa77f6cSMauro Carvalho Chehab /* current cypress EEPROM firmware version */
2890aa77f6cSMauro Carvalho Chehab #define S2255_CUR_USB_FWVER	((3 << 8) | 12)
2900aa77f6cSMauro Carvalho Chehab /* current DSP FW version */
2910aa77f6cSMauro Carvalho Chehab #define S2255_CUR_DSP_FWVER     10104
2920aa77f6cSMauro Carvalho Chehab /* Need DSP version 5+ for video status feature */
2930aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_STATUS      5
2940aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_COLORFILTER 8
295469af77aSHans Verkuil #define S2255_NORMS		(V4L2_STD_ALL)
2960aa77f6cSMauro Carvalho Chehab 
2970aa77f6cSMauro Carvalho Chehab /* private V4L2 controls */
2980aa77f6cSMauro Carvalho Chehab 
2990aa77f6cSMauro Carvalho Chehab /*
3000aa77f6cSMauro Carvalho Chehab  * The following chart displays how COLORFILTER should be set
3010aa77f6cSMauro Carvalho Chehab  *  =========================================================
3020aa77f6cSMauro Carvalho Chehab  *  =     fourcc              =     COLORFILTER             =
3030aa77f6cSMauro Carvalho Chehab  *  =                         ===============================
3040aa77f6cSMauro Carvalho Chehab  *  =                         =   0             =    1      =
3050aa77f6cSMauro Carvalho Chehab  *  =========================================================
3060aa77f6cSMauro Carvalho Chehab  *  =  V4L2_PIX_FMT_GREY(Y8)  = monochrome from = monochrome=
3070aa77f6cSMauro Carvalho Chehab  *  =                         = s-video or      = composite =
3080aa77f6cSMauro Carvalho Chehab  *  =                         = B/W camera      = input     =
3090aa77f6cSMauro Carvalho Chehab  *  =========================================================
3100aa77f6cSMauro Carvalho Chehab  *  =    other                = color, svideo   = color,    =
3110aa77f6cSMauro Carvalho Chehab  *  =                         =                 = composite =
3120aa77f6cSMauro Carvalho Chehab  *  =========================================================
3130aa77f6cSMauro Carvalho Chehab  *
3140aa77f6cSMauro Carvalho Chehab  * Notes:
3150aa77f6cSMauro Carvalho Chehab  *   channels 0-3 on 2255 are composite
3160aa77f6cSMauro Carvalho Chehab  *   channels 0-1 on 2257 are composite, 2-3 are s-video
3170aa77f6cSMauro Carvalho Chehab  * If COLORFILTER is 0 with a composite color camera connected,
3180aa77f6cSMauro Carvalho Chehab  * the output will appear monochrome but hatching
3190aa77f6cSMauro Carvalho Chehab  * will occur.
3200aa77f6cSMauro Carvalho Chehab  * COLORFILTER is different from "color killer" and "color effects"
3210aa77f6cSMauro Carvalho Chehab  * for reasons above.
3220aa77f6cSMauro Carvalho Chehab  */
3230aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_ON  1
3240aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_OFF 0
325192f1e78SHans Verkuil #define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0)
3260aa77f6cSMauro Carvalho Chehab 
3270aa77f6cSMauro Carvalho Chehab /* frame prefix size (sent once every frame) */
3280aa77f6cSMauro Carvalho Chehab #define PREFIX_SIZE		512
3290aa77f6cSMauro Carvalho Chehab 
3300aa77f6cSMauro Carvalho Chehab /* Channels on box are in reverse order */
3310aa77f6cSMauro Carvalho Chehab static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
3320aa77f6cSMauro Carvalho Chehab 
3330aa77f6cSMauro Carvalho Chehab static int debug;
3340aa77f6cSMauro Carvalho Chehab 
3350aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev);
3360aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev);
3375e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc);
3385e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc);
3395e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf,
3400aa77f6cSMauro Carvalho Chehab 			   int jpgsize);
3415e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode);
3420aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev);
343e2a06704SDean A static void s2255_fwload_start(struct s2255_dev *dev);
3440aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev);
3450aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
3460aa77f6cSMauro Carvalho Chehab 			     u16 index, u16 value, void *buf,
3470aa77f6cSMauro Carvalho Chehab 			     s32 buf_len, int bOut);
3480aa77f6cSMauro Carvalho Chehab 
3490aa77f6cSMauro Carvalho Chehab /* dev_err macro with driver name */
3500aa77f6cSMauro Carvalho Chehab #define S2255_DRIVER_NAME "s2255"
3510aa77f6cSMauro Carvalho Chehab #define s2255_dev_err(dev, fmt, arg...)					\
3520aa77f6cSMauro Carvalho Chehab 		dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
3530aa77f6cSMauro Carvalho Chehab 
354f5402007Ssensoray-dev #define dprintk(dev, level, fmt, arg...) \
355f5402007Ssensoray-dev 	v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
3560aa77f6cSMauro Carvalho Chehab 
3570aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver;
3580aa77f6cSMauro Carvalho Chehab 
3590aa77f6cSMauro Carvalho Chehab /* start video number */
3600aa77f6cSMauro Carvalho Chehab static int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
3610aa77f6cSMauro Carvalho Chehab 
3620aa77f6cSMauro Carvalho Chehab /* Enable jpeg capture. */
3630aa77f6cSMauro Carvalho Chehab static int jpeg_enable = 1;
3640aa77f6cSMauro Carvalho Chehab 
3650aa77f6cSMauro Carvalho Chehab module_param(debug, int, 0644);
3660aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
3670aa77f6cSMauro Carvalho Chehab module_param(video_nr, int, 0644);
3680aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
3690aa77f6cSMauro Carvalho Chehab module_param(jpeg_enable, int, 0644);
3700aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
3710aa77f6cSMauro Carvalho Chehab 
3720aa77f6cSMauro Carvalho Chehab /* USB device table */
3730aa77f6cSMauro Carvalho Chehab #define USB_SENSORAY_VID	0x1943
3747fb2e072SArvind Yadav static const struct usb_device_id s2255_table[] = {
3750aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
3760aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
3770aa77f6cSMauro Carvalho Chehab 	{ }			/* Terminating entry */
3780aa77f6cSMauro Carvalho Chehab };
3790aa77f6cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, s2255_table);
3800aa77f6cSMauro Carvalho Chehab 
3810aa77f6cSMauro Carvalho Chehab #define BUFFER_TIMEOUT msecs_to_jiffies(400)
3820aa77f6cSMauro Carvalho Chehab 
3830aa77f6cSMauro Carvalho Chehab /* image formats.  */
3840aa77f6cSMauro Carvalho Chehab /* JPEG formats must be defined last to support jpeg_enable parameter */
3850aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt formats[] = {
3860aa77f6cSMauro Carvalho Chehab 	{
3870aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_YUYV,
3880aa77f6cSMauro Carvalho Chehab 		.depth = 16
3890aa77f6cSMauro Carvalho Chehab 
3900aa77f6cSMauro Carvalho Chehab 	}, {
3910aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_UYVY,
3920aa77f6cSMauro Carvalho Chehab 		.depth = 16
3930aa77f6cSMauro Carvalho Chehab 	}, {
3945c632b22SHans Verkuil 		.fourcc = V4L2_PIX_FMT_YUV422P,
3955c632b22SHans Verkuil 		.depth = 16
3965c632b22SHans Verkuil 
3975c632b22SHans Verkuil 	}, {
3980aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_GREY,
3990aa77f6cSMauro Carvalho Chehab 		.depth = 8
4000aa77f6cSMauro Carvalho Chehab 	}, {
4010aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_JPEG,
4020aa77f6cSMauro Carvalho Chehab 		.depth = 24
4030aa77f6cSMauro Carvalho Chehab 	}, {
4040aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_MJPEG,
4050aa77f6cSMauro Carvalho Chehab 		.depth = 24
4060aa77f6cSMauro Carvalho Chehab 	}
4070aa77f6cSMauro Carvalho Chehab };
4080aa77f6cSMauro Carvalho Chehab 
norm_maxw(struct s2255_vc * vc)4095e950fafSDean Anderson static int norm_maxw(struct s2255_vc *vc)
4100aa77f6cSMauro Carvalho Chehab {
4115e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4120aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
4130aa77f6cSMauro Carvalho Chehab }
4140aa77f6cSMauro Carvalho Chehab 
norm_maxh(struct s2255_vc * vc)4155e950fafSDean Anderson static int norm_maxh(struct s2255_vc *vc)
4160aa77f6cSMauro Carvalho Chehab {
4175e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4180aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
4190aa77f6cSMauro Carvalho Chehab }
4200aa77f6cSMauro Carvalho Chehab 
norm_minw(struct s2255_vc * vc)4215e950fafSDean Anderson static int norm_minw(struct s2255_vc *vc)
4220aa77f6cSMauro Carvalho Chehab {
4235e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4240aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
4250aa77f6cSMauro Carvalho Chehab }
4260aa77f6cSMauro Carvalho Chehab 
norm_minh(struct s2255_vc * vc)4275e950fafSDean Anderson static int norm_minh(struct s2255_vc *vc)
4280aa77f6cSMauro Carvalho Chehab {
4295e950fafSDean Anderson 	return (vc->std & V4L2_STD_525_60) ?
4300aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
4310aa77f6cSMauro Carvalho Chehab }
4320aa77f6cSMauro Carvalho Chehab 
4330aa77f6cSMauro Carvalho Chehab 
4340aa77f6cSMauro Carvalho Chehab /*
4350aa77f6cSMauro Carvalho Chehab  * TODO: fixme: move YUV reordering to hardware
4360aa77f6cSMauro Carvalho Chehab  * converts 2255 planar format to yuyv or uyvy
4370aa77f6cSMauro Carvalho Chehab  */
planar422p_to_yuv_packed(const unsigned char * in,unsigned char * out,int width,int height,int fmt)4380aa77f6cSMauro Carvalho Chehab static void planar422p_to_yuv_packed(const unsigned char *in,
4390aa77f6cSMauro Carvalho Chehab 				     unsigned char *out,
4400aa77f6cSMauro Carvalho Chehab 				     int width, int height,
4410aa77f6cSMauro Carvalho Chehab 				     int fmt)
4420aa77f6cSMauro Carvalho Chehab {
4430aa77f6cSMauro Carvalho Chehab 	unsigned char *pY;
4440aa77f6cSMauro Carvalho Chehab 	unsigned char *pCb;
4450aa77f6cSMauro Carvalho Chehab 	unsigned char *pCr;
4460aa77f6cSMauro Carvalho Chehab 	unsigned long size = height * width;
4470aa77f6cSMauro Carvalho Chehab 	unsigned int i;
4480aa77f6cSMauro Carvalho Chehab 	pY = (unsigned char *)in;
4490aa77f6cSMauro Carvalho Chehab 	pCr = (unsigned char *)in + height * width;
4500aa77f6cSMauro Carvalho Chehab 	pCb = (unsigned char *)in + height * width + (height * width / 2);
4510aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < size * 2; i += 4) {
4520aa77f6cSMauro Carvalho Chehab 		out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
4530aa77f6cSMauro Carvalho Chehab 		out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
4540aa77f6cSMauro Carvalho Chehab 		out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
4550aa77f6cSMauro Carvalho Chehab 		out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
4560aa77f6cSMauro Carvalho Chehab 	}
4570aa77f6cSMauro Carvalho Chehab 	return;
4580aa77f6cSMauro Carvalho Chehab }
4590aa77f6cSMauro Carvalho Chehab 
s2255_reset_dsppower(struct s2255_dev * dev)4600aa77f6cSMauro Carvalho Chehab static void s2255_reset_dsppower(struct s2255_dev *dev)
4610aa77f6cSMauro Carvalho Chehab {
4620aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
463e2a06704SDean A 	msleep(50);
4640aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
4650aa77f6cSMauro Carvalho Chehab 	msleep(600);
4660aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
4670aa77f6cSMauro Carvalho Chehab 	return;
4680aa77f6cSMauro Carvalho Chehab }
4690aa77f6cSMauro Carvalho Chehab 
4700aa77f6cSMauro Carvalho Chehab /* kickstarts the firmware loading. from probe
4710aa77f6cSMauro Carvalho Chehab  */
s2255_timer(struct timer_list * t)47274ee0477SKees Cook static void s2255_timer(struct timer_list *t)
4730aa77f6cSMauro Carvalho Chehab {
47474ee0477SKees Cook 	struct s2255_dev *dev = from_timer(dev, t, timer);
47574ee0477SKees Cook 	struct s2255_fw *data = dev->fw_data;
4760aa77f6cSMauro Carvalho Chehab 	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
477f5402007Ssensoray-dev 		pr_err("s2255: can't submit urb\n");
4780aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
4790aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
4800aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
4810aa77f6cSMauro Carvalho Chehab 		return;
4820aa77f6cSMauro Carvalho Chehab 	}
4830aa77f6cSMauro Carvalho Chehab }
4840aa77f6cSMauro Carvalho Chehab 
4850aa77f6cSMauro Carvalho Chehab 
4860aa77f6cSMauro Carvalho Chehab /* this loads the firmware asynchronously.
4870b84caabSHans Verkuil    Originally this was done synchronously in probe.
4880aa77f6cSMauro Carvalho Chehab    But it is better to load it asynchronously here than block
4890aa77f6cSMauro Carvalho Chehab    inside the probe function. Blocking inside probe affects boot time.
4900aa77f6cSMauro Carvalho Chehab    FW loading is triggered by the timer in the probe function
4910aa77f6cSMauro Carvalho Chehab */
s2255_fwchunk_complete(struct urb * urb)4920aa77f6cSMauro Carvalho Chehab static void s2255_fwchunk_complete(struct urb *urb)
4930aa77f6cSMauro Carvalho Chehab {
4940aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = urb->context;
4950aa77f6cSMauro Carvalho Chehab 	struct usb_device *udev = urb->dev;
4960aa77f6cSMauro Carvalho Chehab 	int len;
4970aa77f6cSMauro Carvalho Chehab 	if (urb->status) {
4980aa77f6cSMauro Carvalho Chehab 		dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
4990aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5000aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5010aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5020aa77f6cSMauro Carvalho Chehab 		return;
5030aa77f6cSMauro Carvalho Chehab 	}
5040aa77f6cSMauro Carvalho Chehab 	if (data->fw_urb == NULL) {
5050aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&udev->dev, "disconnected\n");
5060aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5070aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5080aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5090aa77f6cSMauro Carvalho Chehab 		return;
5100aa77f6cSMauro Carvalho Chehab 	}
5110aa77f6cSMauro Carvalho Chehab #define CHUNK_SIZE 512
5120aa77f6cSMauro Carvalho Chehab 	/* all USB transfers must be done with continuous kernel memory.
5130aa77f6cSMauro Carvalho Chehab 	   can't allocate more than 128k in current linux kernel, so
5140aa77f6cSMauro Carvalho Chehab 	   upload the firmware in chunks
5150aa77f6cSMauro Carvalho Chehab 	 */
5160aa77f6cSMauro Carvalho Chehab 	if (data->fw_loaded < data->fw_size) {
5170aa77f6cSMauro Carvalho Chehab 		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
5180aa77f6cSMauro Carvalho Chehab 		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
5190aa77f6cSMauro Carvalho Chehab 
5200aa77f6cSMauro Carvalho Chehab 		if (len < CHUNK_SIZE)
5210aa77f6cSMauro Carvalho Chehab 			memset(data->pfw_data, 0, CHUNK_SIZE);
5220aa77f6cSMauro Carvalho Chehab 
5230aa77f6cSMauro Carvalho Chehab 		memcpy(data->pfw_data,
5240aa77f6cSMauro Carvalho Chehab 		       (char *) data->fw->data + data->fw_loaded, len);
5250aa77f6cSMauro Carvalho Chehab 
5260aa77f6cSMauro Carvalho Chehab 		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
5270aa77f6cSMauro Carvalho Chehab 				  data->pfw_data, CHUNK_SIZE,
5280aa77f6cSMauro Carvalho Chehab 				  s2255_fwchunk_complete, data);
5290aa77f6cSMauro Carvalho Chehab 		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
5300aa77f6cSMauro Carvalho Chehab 			dev_err(&udev->dev, "failed submit URB\n");
5310aa77f6cSMauro Carvalho Chehab 			atomic_set(&data->fw_state, S2255_FW_FAILED);
5320aa77f6cSMauro Carvalho Chehab 			/* wake up anything waiting for the firmware */
5330aa77f6cSMauro Carvalho Chehab 			wake_up(&data->wait_fw);
5340aa77f6cSMauro Carvalho Chehab 			return;
5350aa77f6cSMauro Carvalho Chehab 		}
5360aa77f6cSMauro Carvalho Chehab 		data->fw_loaded += len;
537f5402007Ssensoray-dev 	} else
5380aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
5390aa77f6cSMauro Carvalho Chehab 	return;
5400aa77f6cSMauro Carvalho Chehab 
5410aa77f6cSMauro Carvalho Chehab }
5420aa77f6cSMauro Carvalho Chehab 
s2255_got_frame(struct s2255_vc * vc,int jpgsize)5439694fbecSsensoray-dev static void s2255_got_frame(struct s2255_vc *vc, int jpgsize)
5440aa77f6cSMauro Carvalho Chehab {
5450aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf;
5465e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
5470aa77f6cSMauro Carvalho Chehab 	unsigned long flags = 0;
5489694fbecSsensoray-dev 
549340a30c5Ssensoray-dev 	spin_lock_irqsave(&vc->qlock, flags);
5505e950fafSDean Anderson 	if (list_empty(&vc->buf_list)) {
551f5402007Ssensoray-dev 		dprintk(dev, 1, "No active queue to serve\n");
5529694fbecSsensoray-dev 		spin_unlock_irqrestore(&vc->qlock, flags);
5539694fbecSsensoray-dev 		return;
5540aa77f6cSMauro Carvalho Chehab 	}
5555e950fafSDean Anderson 	buf = list_entry(vc->buf_list.next,
556340a30c5Ssensoray-dev 			 struct s2255_buffer, list);
557340a30c5Ssensoray-dev 	list_del(&buf->list);
558d6dd645eSJunghak Sung 	buf->vb.vb2_buf.timestamp = ktime_get_ns();
5592d700715SJunghak Sung 	buf->vb.field = vc->field;
5602d700715SJunghak Sung 	buf->vb.sequence = vc->frame_count;
561340a30c5Ssensoray-dev 	spin_unlock_irqrestore(&vc->qlock, flags);
5629694fbecSsensoray-dev 
5639694fbecSsensoray-dev 	s2255_fillbuff(vc, buf, jpgsize);
5649694fbecSsensoray-dev 	/* tell v4l buffer was filled */
5652d700715SJunghak Sung 	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
5669694fbecSsensoray-dev 	dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
5670aa77f6cSMauro Carvalho Chehab }
5680aa77f6cSMauro Carvalho Chehab 
format_by_fourcc(int fourcc)5690aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt *format_by_fourcc(int fourcc)
5700aa77f6cSMauro Carvalho Chehab {
5710aa77f6cSMauro Carvalho Chehab 	unsigned int i;
5720aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
5730aa77f6cSMauro Carvalho Chehab 		if (-1 == formats[i].fourcc)
5740aa77f6cSMauro Carvalho Chehab 			continue;
5750aa77f6cSMauro Carvalho Chehab 		if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
5760aa77f6cSMauro Carvalho Chehab 				     (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
5770aa77f6cSMauro Carvalho Chehab 			continue;
5780aa77f6cSMauro Carvalho Chehab 		if (formats[i].fourcc == fourcc)
5790aa77f6cSMauro Carvalho Chehab 			return formats + i;
5800aa77f6cSMauro Carvalho Chehab 	}
5810aa77f6cSMauro Carvalho Chehab 	return NULL;
5820aa77f6cSMauro Carvalho Chehab }
5830aa77f6cSMauro Carvalho Chehab 
5840aa77f6cSMauro Carvalho Chehab /* video buffer vmalloc implementation based partly on VIVI driver which is
5850aa77f6cSMauro Carvalho Chehab  *          Copyright (c) 2006 by
5860aa77f6cSMauro Carvalho Chehab  *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
5870aa77f6cSMauro Carvalho Chehab  *                  Ted Walther <ted--a.t--enumera.com>
5880aa77f6cSMauro Carvalho Chehab  *                  John Sokol <sokol--a.t--videotechnology.com>
5890aa77f6cSMauro Carvalho Chehab  *                  http://v4l.videotechnology.com/
5900aa77f6cSMauro Carvalho Chehab  *
5910aa77f6cSMauro Carvalho Chehab  */
s2255_fillbuff(struct s2255_vc * vc,struct s2255_buffer * buf,int jpgsize)5925e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc,
5930aa77f6cSMauro Carvalho Chehab 			   struct s2255_buffer *buf, int jpgsize)
5940aa77f6cSMauro Carvalho Chehab {
5950aa77f6cSMauro Carvalho Chehab 	int pos = 0;
5960aa77f6cSMauro Carvalho Chehab 	const char *tmpbuf;
5972d700715SJunghak Sung 	char *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
5980aa77f6cSMauro Carvalho Chehab 	unsigned long last_frame;
5995e950fafSDean Anderson 	struct s2255_dev *dev = vc->dev;
6000aa77f6cSMauro Carvalho Chehab 
6010aa77f6cSMauro Carvalho Chehab 	if (!vbuf)
6020aa77f6cSMauro Carvalho Chehab 		return;
6035e950fafSDean Anderson 	last_frame = vc->last_frame;
6040aa77f6cSMauro Carvalho Chehab 	if (last_frame != -1) {
6050aa77f6cSMauro Carvalho Chehab 		tmpbuf =
6065e950fafSDean Anderson 		    (const char *)vc->buffer.frame[last_frame].lpvbits;
6078bf405a0SDean Anderson 		switch (vc->fmt->fourcc) {
6080aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUYV:
6090aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_UYVY:
6100aa77f6cSMauro Carvalho Chehab 			planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
611340a30c5Ssensoray-dev 						 vbuf, vc->width,
612340a30c5Ssensoray-dev 						 vc->height,
6138bf405a0SDean Anderson 						 vc->fmt->fourcc);
6140aa77f6cSMauro Carvalho Chehab 			break;
6150aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_GREY:
616340a30c5Ssensoray-dev 			memcpy(vbuf, tmpbuf, vc->width * vc->height);
6170aa77f6cSMauro Carvalho Chehab 			break;
6180aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_JPEG:
6190aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_MJPEG:
6202d700715SJunghak Sung 			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
621340a30c5Ssensoray-dev 			memcpy(vbuf, tmpbuf, jpgsize);
6220aa77f6cSMauro Carvalho Chehab 			break;
6230aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUV422P:
6240aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf,
625340a30c5Ssensoray-dev 			       vc->width * vc->height * 2);
6260aa77f6cSMauro Carvalho Chehab 			break;
6270aa77f6cSMauro Carvalho Chehab 		default:
628f5402007Ssensoray-dev 			pr_info("s2255: unknown format?\n");
6290aa77f6cSMauro Carvalho Chehab 		}
6305e950fafSDean Anderson 		vc->last_frame = -1;
6310aa77f6cSMauro Carvalho Chehab 	} else {
632f5402007Ssensoray-dev 		pr_err("s2255: =======no frame\n");
6330aa77f6cSMauro Carvalho Chehab 		return;
6340aa77f6cSMauro Carvalho Chehab 	}
63586f181c7SMauro Carvalho Chehab 	dprintk(dev, 2, "s2255fill at : Buffer %p size= %d\n",
63686f181c7SMauro Carvalho Chehab 		vbuf, pos);
6370aa77f6cSMauro Carvalho Chehab }
6380aa77f6cSMauro Carvalho Chehab 
6390aa77f6cSMauro Carvalho Chehab 
6400aa77f6cSMauro Carvalho Chehab /* ------------------------------------------------------------------
6410aa77f6cSMauro Carvalho Chehab    Videobuf operations
6420aa77f6cSMauro Carvalho Chehab    ------------------------------------------------------------------*/
6430aa77f6cSMauro Carvalho Chehab 
queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])644df9ecb0cSHans Verkuil static int queue_setup(struct vb2_queue *vq,
645340a30c5Ssensoray-dev 		       unsigned int *nbuffers, unsigned int *nplanes,
64636c0f8b3SHans Verkuil 		       unsigned int sizes[], struct device *alloc_devs[])
6470aa77f6cSMauro Carvalho Chehab {
648340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vq);
6499da62eb0SDean Anderson 	if (*nbuffers < S2255_MIN_BUFS)
6509da62eb0SDean Anderson 		*nbuffers = S2255_MIN_BUFS;
651340a30c5Ssensoray-dev 	*nplanes = 1;
652340a30c5Ssensoray-dev 	sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3);
6530aa77f6cSMauro Carvalho Chehab 	return 0;
6540aa77f6cSMauro Carvalho Chehab }
6550aa77f6cSMauro Carvalho Chehab 
buffer_prepare(struct vb2_buffer * vb)656340a30c5Ssensoray-dev static int buffer_prepare(struct vb2_buffer *vb)
6570aa77f6cSMauro Carvalho Chehab {
658340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
6592d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6602d700715SJunghak Sung 	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
6615e950fafSDean Anderson 	int w = vc->width;
6625e950fafSDean Anderson 	int h = vc->height;
663340a30c5Ssensoray-dev 	unsigned long size;
664340a30c5Ssensoray-dev 
665340a30c5Ssensoray-dev 	dprintk(vc->dev, 4, "%s\n", __func__);
6665e950fafSDean Anderson 	if (vc->fmt == NULL)
6670aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
6680aa77f6cSMauro Carvalho Chehab 
6695e950fafSDean Anderson 	if ((w < norm_minw(vc)) ||
6705e950fafSDean Anderson 	    (w > norm_maxw(vc)) ||
6715e950fafSDean Anderson 	    (h < norm_minh(vc)) ||
6725e950fafSDean Anderson 	    (h > norm_maxh(vc))) {
67392cde477SDean Anderson 		dprintk(vc->dev, 4, "invalid buffer prepare\n");
6740aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
6750aa77f6cSMauro Carvalho Chehab 	}
676340a30c5Ssensoray-dev 	size = w * h * (vc->fmt->depth >> 3);
677340a30c5Ssensoray-dev 	if (vb2_plane_size(vb, 0) < size) {
67892cde477SDean Anderson 		dprintk(vc->dev, 4, "invalid buffer prepare\n");
6790aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
6800aa77f6cSMauro Carvalho Chehab 	}
6810aa77f6cSMauro Carvalho Chehab 
6822d700715SJunghak Sung 	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
6830aa77f6cSMauro Carvalho Chehab 	return 0;
6840aa77f6cSMauro Carvalho Chehab }
6850aa77f6cSMauro Carvalho Chehab 
buffer_queue(struct vb2_buffer * vb)686340a30c5Ssensoray-dev static void buffer_queue(struct vb2_buffer *vb)
6870aa77f6cSMauro Carvalho Chehab {
6882d700715SJunghak Sung 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6892d700715SJunghak Sung 	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
690340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
691340a30c5Ssensoray-dev 	unsigned long flags = 0;
69292cde477SDean Anderson 	dprintk(vc->dev, 1, "%s\n", __func__);
693340a30c5Ssensoray-dev 	spin_lock_irqsave(&vc->qlock, flags);
694340a30c5Ssensoray-dev 	list_add_tail(&buf->list, &vc->buf_list);
695340a30c5Ssensoray-dev 	spin_unlock_irqrestore(&vc->qlock, flags);
6960aa77f6cSMauro Carvalho Chehab }
6970aa77f6cSMauro Carvalho Chehab 
698340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count);
699e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq);
7000aa77f6cSMauro Carvalho Chehab 
7011bc17717SJulia Lawall static const struct vb2_ops s2255_video_qops = {
702340a30c5Ssensoray-dev 	.queue_setup = queue_setup,
7030aa77f6cSMauro Carvalho Chehab 	.buf_prepare = buffer_prepare,
7040aa77f6cSMauro Carvalho Chehab 	.buf_queue = buffer_queue,
705340a30c5Ssensoray-dev 	.start_streaming = start_streaming,
706340a30c5Ssensoray-dev 	.stop_streaming = stop_streaming,
707340a30c5Ssensoray-dev 	.wait_prepare = vb2_ops_wait_prepare,
708340a30c5Ssensoray-dev 	.wait_finish = vb2_ops_wait_finish,
7090aa77f6cSMauro Carvalho Chehab };
7100aa77f6cSMauro Carvalho Chehab 
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * cap)7110aa77f6cSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv,
7120aa77f6cSMauro Carvalho Chehab 			   struct v4l2_capability *cap)
7130aa77f6cSMauro Carvalho Chehab {
714340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
715340a30c5Ssensoray-dev 	struct s2255_dev *dev = vc->dev;
71639696009SHans Verkuil 
717c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, "s2255", sizeof(cap->driver));
718c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, "s2255", sizeof(cap->card));
7190aa77f6cSMauro Carvalho Chehab 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
7200aa77f6cSMauro Carvalho Chehab 	return 0;
7210aa77f6cSMauro Carvalho Chehab }
7220aa77f6cSMauro Carvalho Chehab 
vidioc_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)7230aa77f6cSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
7240aa77f6cSMauro Carvalho Chehab 			       struct v4l2_fmtdesc *f)
7250aa77f6cSMauro Carvalho Chehab {
7260aa77f6cSMauro Carvalho Chehab 	int index = f->index;
7270aa77f6cSMauro Carvalho Chehab 
7280aa77f6cSMauro Carvalho Chehab 	if (index >= ARRAY_SIZE(formats))
7290aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7300aa77f6cSMauro Carvalho Chehab 	if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
7310aa77f6cSMauro Carvalho Chehab 			(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
7320aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7330aa77f6cSMauro Carvalho Chehab 	f->pixelformat = formats[index].fourcc;
7340aa77f6cSMauro Carvalho Chehab 	return 0;
7350aa77f6cSMauro Carvalho Chehab }
7360aa77f6cSMauro Carvalho Chehab 
vidioc_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)7370aa77f6cSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
7380aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
7390aa77f6cSMauro Carvalho Chehab {
740340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
7415e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
7420aa77f6cSMauro Carvalho Chehab 
7435e950fafSDean Anderson 	f->fmt.pix.width = vc->width;
7445e950fafSDean Anderson 	f->fmt.pix.height = vc->height;
74592513611SHans Verkuil 	if (f->fmt.pix.height >=
74692513611SHans Verkuil 	    (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2)
74792513611SHans Verkuil 		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
74892513611SHans Verkuil 	else
74992513611SHans Verkuil 		f->fmt.pix.field = V4L2_FIELD_TOP;
7505e950fafSDean Anderson 	f->fmt.pix.pixelformat = vc->fmt->fourcc;
7515e950fafSDean Anderson 	f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3);
7520aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
75329ceb110SHans Verkuil 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
7540aa77f6cSMauro Carvalho Chehab 	return 0;
7550aa77f6cSMauro Carvalho Chehab }
7560aa77f6cSMauro Carvalho Chehab 
vidioc_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)7570aa77f6cSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
7580aa77f6cSMauro Carvalho Chehab 			      struct v4l2_format *f)
7590aa77f6cSMauro Carvalho Chehab {
7600aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
7610aa77f6cSMauro Carvalho Chehab 	enum v4l2_field field;
762340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
7635e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
7640aa77f6cSMauro Carvalho Chehab 
7650aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
7660aa77f6cSMauro Carvalho Chehab 
7670aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
7680aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7690aa77f6cSMauro Carvalho Chehab 
77092cde477SDean Anderson 	dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n",
7710aa77f6cSMauro Carvalho Chehab 		__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
7720aa77f6cSMauro Carvalho Chehab 	if (is_ntsc) {
7730aa77f6cSMauro Carvalho Chehab 		/* NTSC */
7740aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
7750aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
77692513611SHans Verkuil 			field = V4L2_FIELD_INTERLACED;
7770aa77f6cSMauro Carvalho Chehab 		} else {
7780aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
7790aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
7800aa77f6cSMauro Carvalho Chehab 		}
7810aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
7820aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
7830aa77f6cSMauro Carvalho Chehab 		else
7840aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
7850aa77f6cSMauro Carvalho Chehab 	} else {
7860aa77f6cSMauro Carvalho Chehab 		/* PAL */
7870aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
7880aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
78992513611SHans Verkuil 			field = V4L2_FIELD_INTERLACED;
7900aa77f6cSMauro Carvalho Chehab 		} else {
7910aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
7920aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
7930aa77f6cSMauro Carvalho Chehab 		}
79492513611SHans Verkuil 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL)
7950aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
79692513611SHans Verkuil 		else
7970aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
7980aa77f6cSMauro Carvalho Chehab 	}
7990aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.field = field;
8000aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
8010aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
80229ceb110SHans Verkuil 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
80392cde477SDean Anderson 	dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__,
8040aa77f6cSMauro Carvalho Chehab 		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
8050aa77f6cSMauro Carvalho Chehab 	return 0;
8060aa77f6cSMauro Carvalho Chehab }
8070aa77f6cSMauro Carvalho Chehab 
vidioc_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)8080aa77f6cSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
8090aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
8100aa77f6cSMauro Carvalho Chehab {
811340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
8120aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
813340a30c5Ssensoray-dev 	struct vb2_queue *q = &vc->vb_vidq;
8140aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
8150aa77f6cSMauro Carvalho Chehab 	int ret;
8160aa77f6cSMauro Carvalho Chehab 
817340a30c5Ssensoray-dev 	ret = vidioc_try_fmt_vid_cap(file, vc, f);
8180aa77f6cSMauro Carvalho Chehab 
8190aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
8200aa77f6cSMauro Carvalho Chehab 		return ret;
8210aa77f6cSMauro Carvalho Chehab 
8220aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
8230aa77f6cSMauro Carvalho Chehab 
8240aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
8250aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8260aa77f6cSMauro Carvalho Chehab 
827340a30c5Ssensoray-dev 	if (vb2_is_busy(q)) {
82892cde477SDean Anderson 		dprintk(vc->dev, 1, "queue busy\n");
829340a30c5Ssensoray-dev 		return -EBUSY;
8300aa77f6cSMauro Carvalho Chehab 	}
8310aa77f6cSMauro Carvalho Chehab 
8325e950fafSDean Anderson 	mode = vc->mode;
8335e950fafSDean Anderson 	vc->fmt = fmt;
8345e950fafSDean Anderson 	vc->width = f->fmt.pix.width;
8355e950fafSDean Anderson 	vc->height = f->fmt.pix.height;
836340a30c5Ssensoray-dev 	vc->field = f->fmt.pix.field;
8375e950fafSDean Anderson 	if (vc->width > norm_minw(vc)) {
8385e950fafSDean Anderson 		if (vc->height > norm_minh(vc)) {
8395e950fafSDean Anderson 			if (vc->cap_parm.capturemode &
8400aa77f6cSMauro Carvalho Chehab 			    V4L2_MODE_HIGHQUALITY)
8410aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFSI;
8420aa77f6cSMauro Carvalho Chehab 			else
8430aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFS;
8440aa77f6cSMauro Carvalho Chehab 		} else
8450aa77f6cSMauro Carvalho Chehab 			mode.scale = SCALE_2CIFS;
8460aa77f6cSMauro Carvalho Chehab 
8470aa77f6cSMauro Carvalho Chehab 	} else {
8480aa77f6cSMauro Carvalho Chehab 		mode.scale = SCALE_1CIFS;
8490aa77f6cSMauro Carvalho Chehab 	}
8500aa77f6cSMauro Carvalho Chehab 	/* color mode */
8515e950fafSDean Anderson 	switch (vc->fmt->fourcc) {
8520aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_GREY:
8530aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8540aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_Y8;
8550aa77f6cSMauro Carvalho Chehab 		break;
8560aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_JPEG:
8570aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_MJPEG:
8580aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8590aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_JPG;
8605e950fafSDean Anderson 		mode.color |= (vc->jpegqual << 8);
8610aa77f6cSMauro Carvalho Chehab 		break;
8620aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUV422P:
8630aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8640aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPL;
8650aa77f6cSMauro Carvalho Chehab 		break;
8660aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUYV:
8670aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_UYVY:
8680aa77f6cSMauro Carvalho Chehab 	default:
8690aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
8700aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPK;
8710aa77f6cSMauro Carvalho Chehab 		break;
8720aa77f6cSMauro Carvalho Chehab 	}
8735e950fafSDean Anderson 	if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR))
8740aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
8755e950fafSDean Anderson 	else if (mode.scale != vc->mode.scale)
8760aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
8775e950fafSDean Anderson 	else if (mode.format != vc->mode.format)
8780aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
8795e950fafSDean Anderson 	vc->mode = mode;
8805e950fafSDean Anderson 	(void) s2255_set_mode(vc, &mode);
881340a30c5Ssensoray-dev 	return 0;
8820aa77f6cSMauro Carvalho Chehab }
8830aa77f6cSMauro Carvalho Chehab 
8840aa77f6cSMauro Carvalho Chehab 
8850aa77f6cSMauro Carvalho Chehab /* write to the configuration pipe, synchronously */
s2255_write_config(struct usb_device * udev,unsigned char * pbuf,int size)8860aa77f6cSMauro Carvalho Chehab static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
8870aa77f6cSMauro Carvalho Chehab 			      int size)
8880aa77f6cSMauro Carvalho Chehab {
8890aa77f6cSMauro Carvalho Chehab 	int pipe;
8900aa77f6cSMauro Carvalho Chehab 	int done;
8910aa77f6cSMauro Carvalho Chehab 	long retval = -1;
8920aa77f6cSMauro Carvalho Chehab 	if (udev) {
8930aa77f6cSMauro Carvalho Chehab 		pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
8940aa77f6cSMauro Carvalho Chehab 		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
8950aa77f6cSMauro Carvalho Chehab 	}
8960aa77f6cSMauro Carvalho Chehab 	return retval;
8970aa77f6cSMauro Carvalho Chehab }
8980aa77f6cSMauro Carvalho Chehab 
get_transfer_size(struct s2255_mode * mode)8990aa77f6cSMauro Carvalho Chehab static u32 get_transfer_size(struct s2255_mode *mode)
9000aa77f6cSMauro Carvalho Chehab {
9010aa77f6cSMauro Carvalho Chehab 	int linesPerFrame = LINE_SZ_DEF;
9020aa77f6cSMauro Carvalho Chehab 	int pixelsPerLine = NUM_LINES_DEF;
9030aa77f6cSMauro Carvalho Chehab 	u32 outImageSize;
9040aa77f6cSMauro Carvalho Chehab 	u32 usbInSize;
9050aa77f6cSMauro Carvalho Chehab 	unsigned int mask_mult;
9060aa77f6cSMauro Carvalho Chehab 
9070aa77f6cSMauro Carvalho Chehab 	if (mode == NULL)
9080aa77f6cSMauro Carvalho Chehab 		return 0;
9090aa77f6cSMauro Carvalho Chehab 
9100aa77f6cSMauro Carvalho Chehab 	if (mode->format == FORMAT_NTSC) {
9110aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
9120aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
9130aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
9140aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
9150aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
9160aa77f6cSMauro Carvalho Chehab 			break;
9170aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
9180aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_NTSC;
9190aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
9200aa77f6cSMauro Carvalho Chehab 			break;
9210aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
9220aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_NTSC;
9230aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
9240aa77f6cSMauro Carvalho Chehab 			break;
9250aa77f6cSMauro Carvalho Chehab 		default:
9260aa77f6cSMauro Carvalho Chehab 			break;
9270aa77f6cSMauro Carvalho Chehab 		}
9280aa77f6cSMauro Carvalho Chehab 	} else if (mode->format == FORMAT_PAL) {
9290aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
9300aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
9310aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
9320aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
9330aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_PAL;
9340aa77f6cSMauro Carvalho Chehab 			break;
9350aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
9360aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_PAL;
9370aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_PAL;
9380aa77f6cSMauro Carvalho Chehab 			break;
9390aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
9400aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_PAL;
9410aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_PAL;
9420aa77f6cSMauro Carvalho Chehab 			break;
9430aa77f6cSMauro Carvalho Chehab 		default:
9440aa77f6cSMauro Carvalho Chehab 			break;
9450aa77f6cSMauro Carvalho Chehab 		}
9460aa77f6cSMauro Carvalho Chehab 	}
9470aa77f6cSMauro Carvalho Chehab 	outImageSize = linesPerFrame * pixelsPerLine;
9480aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) != COLOR_Y8) {
9490aa77f6cSMauro Carvalho Chehab 		/* 2 bytes/pixel if not monochrome */
9500aa77f6cSMauro Carvalho Chehab 		outImageSize *= 2;
9510aa77f6cSMauro Carvalho Chehab 	}
9520aa77f6cSMauro Carvalho Chehab 
9530aa77f6cSMauro Carvalho Chehab 	/* total bytes to send including prefix and 4K padding;
9540aa77f6cSMauro Carvalho Chehab 	   must be a multiple of USB_READ_SIZE */
9550aa77f6cSMauro Carvalho Chehab 	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
9560aa77f6cSMauro Carvalho Chehab 	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
9570aa77f6cSMauro Carvalho Chehab 	/* if size not a multiple of USB_READ_SIZE */
9580aa77f6cSMauro Carvalho Chehab 	if (usbInSize & ~mask_mult)
9590aa77f6cSMauro Carvalho Chehab 		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
9600aa77f6cSMauro Carvalho Chehab 	return usbInSize;
9610aa77f6cSMauro Carvalho Chehab }
9620aa77f6cSMauro Carvalho Chehab 
s2255_print_cfg(struct s2255_dev * sdev,struct s2255_mode * mode)9630aa77f6cSMauro Carvalho Chehab static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
9640aa77f6cSMauro Carvalho Chehab {
9650aa77f6cSMauro Carvalho Chehab 	struct device *dev = &sdev->udev->dev;
9660aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
9670aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
9680aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
9690aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "bright: 0x%x\n", mode->bright);
9700aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
9710aa77f6cSMauro Carvalho Chehab }
9720aa77f6cSMauro Carvalho Chehab 
9730aa77f6cSMauro Carvalho Chehab /*
9740aa77f6cSMauro Carvalho Chehab  * set mode is the function which controls the DSP.
9750aa77f6cSMauro Carvalho Chehab  * the restart parameter in struct s2255_mode should be set whenever
9760aa77f6cSMauro Carvalho Chehab  * the image size could change via color format, video system or image
9770aa77f6cSMauro Carvalho Chehab  * size.
9780aa77f6cSMauro Carvalho Chehab  * When the restart parameter is set, we sleep for ONE frame to allow the
9790aa77f6cSMauro Carvalho Chehab  * DSP time to get the new frame
9800aa77f6cSMauro Carvalho Chehab  */
s2255_set_mode(struct s2255_vc * vc,struct s2255_mode * mode)9815e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc,
9820aa77f6cSMauro Carvalho Chehab 			  struct s2255_mode *mode)
9830aa77f6cSMauro Carvalho Chehab {
9840aa77f6cSMauro Carvalho Chehab 	int res;
9850aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
9865e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
9870b84caabSHans Verkuil 	int i;
98847d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
9890b84caabSHans Verkuil 
99047d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
9915e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
9925e950fafSDean Anderson 	dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx);
9930aa77f6cSMauro Carvalho Chehab 	/* if JPEG, set the quality */
9940aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) == COLOR_JPG) {
9950aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_COLOR;
9960aa77f6cSMauro Carvalho Chehab 		mode->color |= COLOR_JPG;
9970aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_JPG_QUALITY;
9985e950fafSDean Anderson 		mode->color |= (vc->jpegqual << 8);
9990aa77f6cSMauro Carvalho Chehab 	}
10000aa77f6cSMauro Carvalho Chehab 	/* save the mode */
10015e950fafSDean Anderson 	vc->mode = *mode;
10025e950fafSDean Anderson 	vc->req_image_size = get_transfer_size(mode);
10035e950fafSDean Anderson 	dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size);
10040aa77f6cSMauro Carvalho Chehab 	/* set the mode */
10050aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
10060aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
10070aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_SET_MODE;
10080b84caabSHans Verkuil 	for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++)
10095e950fafSDean Anderson 		buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]);
10105e950fafSDean Anderson 	vc->setmode_ready = 0;
10110aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
10120aa77f6cSMauro Carvalho Chehab 	if (debug)
10130aa77f6cSMauro Carvalho Chehab 		s2255_print_cfg(dev, mode);
10140aa77f6cSMauro Carvalho Chehab 	/* wait at least 3 frames before continuing */
10150aa77f6cSMauro Carvalho Chehab 	if (mode->restart) {
10165e950fafSDean Anderson 		wait_event_timeout(vc->wait_setmode,
10175e950fafSDean Anderson 				   (vc->setmode_ready != 0),
10180aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
10195e950fafSDean Anderson 		if (vc->setmode_ready != 1) {
1020f5402007Ssensoray-dev 			dprintk(dev, 0, "s2255: no set mode response\n");
10210aa77f6cSMauro Carvalho Chehab 			res = -EFAULT;
10220aa77f6cSMauro Carvalho Chehab 		}
10230aa77f6cSMauro Carvalho Chehab 	}
10240aa77f6cSMauro Carvalho Chehab 	/* clear the restart flag */
10255e950fafSDean Anderson 	vc->mode.restart = 0;
10265e950fafSDean Anderson 	dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res);
102747d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
10280aa77f6cSMauro Carvalho Chehab 	return res;
10290aa77f6cSMauro Carvalho Chehab }
10300aa77f6cSMauro Carvalho Chehab 
s2255_cmd_status(struct s2255_vc * vc,u32 * pstatus)10315e950fafSDean Anderson static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus)
10320aa77f6cSMauro Carvalho Chehab {
10330aa77f6cSMauro Carvalho Chehab 	int res;
10340aa77f6cSMauro Carvalho Chehab 	u32 chn_rev;
10355e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
103647d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
103747d8c881SDean Anderson 
103847d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
10395e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
10405e950fafSDean Anderson 	dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx);
10410aa77f6cSMauro Carvalho Chehab 	/* form the get vid status command */
10420aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
10430aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
10440aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_STATUS;
10450aa77f6cSMauro Carvalho Chehab 	*pstatus = 0;
10465e950fafSDean Anderson 	vc->vidstatus_ready = 0;
10470aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
10485e950fafSDean Anderson 	wait_event_timeout(vc->wait_vidstatus,
10495e950fafSDean Anderson 			   (vc->vidstatus_ready != 0),
10500aa77f6cSMauro Carvalho Chehab 			   msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
10515e950fafSDean Anderson 	if (vc->vidstatus_ready != 1) {
1052f5402007Ssensoray-dev 		dprintk(dev, 0, "s2255: no vidstatus response\n");
10530aa77f6cSMauro Carvalho Chehab 		res = -EFAULT;
10540aa77f6cSMauro Carvalho Chehab 	}
10555e950fafSDean Anderson 	*pstatus = vc->vidstatus;
1056f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus);
105747d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
10580aa77f6cSMauro Carvalho Chehab 	return res;
10590aa77f6cSMauro Carvalho Chehab }
10600aa77f6cSMauro Carvalho Chehab 
start_streaming(struct vb2_queue * vq,unsigned int count)1061340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count)
10620aa77f6cSMauro Carvalho Chehab {
1063340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vq);
10640aa77f6cSMauro Carvalho Chehab 	int j;
106592cde477SDean Anderson 
10665e950fafSDean Anderson 	vc->last_frame = -1;
10675e950fafSDean Anderson 	vc->bad_payload = 0;
10685e950fafSDean Anderson 	vc->cur_frame = 0;
10695e950fafSDean Anderson 	vc->frame_count = 0;
10700aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
10715e950fafSDean Anderson 		vc->buffer.frame[j].ulState = S2255_READ_IDLE;
10725e950fafSDean Anderson 		vc->buffer.frame[j].cur_size = 0;
10730aa77f6cSMauro Carvalho Chehab 	}
1074340a30c5Ssensoray-dev 	return s2255_start_acquire(vc);
10750aa77f6cSMauro Carvalho Chehab }
10760aa77f6cSMauro Carvalho Chehab 
1077340a30c5Ssensoray-dev /* abort streaming and wait for last buffer */
stop_streaming(struct vb2_queue * vq)1078e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq)
10790aa77f6cSMauro Carvalho Chehab {
1080340a30c5Ssensoray-dev 	struct s2255_vc *vc = vb2_get_drv_priv(vq);
1081340a30c5Ssensoray-dev 	struct s2255_buffer *buf, *node;
1082340a30c5Ssensoray-dev 	unsigned long flags;
1083340a30c5Ssensoray-dev 	(void) s2255_stop_acquire(vc);
1084340a30c5Ssensoray-dev 	spin_lock_irqsave(&vc->qlock, flags);
1085340a30c5Ssensoray-dev 	list_for_each_entry_safe(buf, node, &vc->buf_list, list) {
1086340a30c5Ssensoray-dev 		list_del(&buf->list);
10872d700715SJunghak Sung 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
1088340a30c5Ssensoray-dev 		dprintk(vc->dev, 2, "[%p/%d] done\n",
10892d700715SJunghak Sung 			buf, buf->vb.vb2_buf.index);
1090340a30c5Ssensoray-dev 	}
1091340a30c5Ssensoray-dev 	spin_unlock_irqrestore(&vc->qlock, flags);
10920aa77f6cSMauro Carvalho Chehab }
10930aa77f6cSMauro Carvalho Chehab 
vidioc_s_std(struct file * file,void * priv,v4l2_std_id i)1094314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
10950aa77f6cSMauro Carvalho Chehab {
1096340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
10970aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
1098340a30c5Ssensoray-dev 	struct vb2_queue *q = &vc->vb_vidq;
1099469af77aSHans Verkuil 
1100340a30c5Ssensoray-dev 	/*
1101340a30c5Ssensoray-dev 	 * Changing the standard implies a format change, which is not allowed
1102340a30c5Ssensoray-dev 	 * while buffers for use with streaming have already been allocated.
1103340a30c5Ssensoray-dev 	 */
1104340a30c5Ssensoray-dev 	if (vb2_is_busy(q))
1105340a30c5Ssensoray-dev 		return -EBUSY;
1106340a30c5Ssensoray-dev 
110792cde477SDean Anderson 	mode = vc->mode;
1108314527acSHans Verkuil 	if (i & V4L2_STD_525_60) {
110992cde477SDean Anderson 		dprintk(vc->dev, 4, "%s 60 Hz\n", __func__);
11100aa77f6cSMauro Carvalho Chehab 		/* if changing format, reset frame decimation/intervals */
11110aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_NTSC) {
11120aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
11130aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_NTSC;
11140aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
11155e950fafSDean Anderson 			vc->width = LINE_SZ_4CIFS_NTSC;
11165e950fafSDean Anderson 			vc->height = NUM_LINES_4CIFS_NTSC * 2;
11170aa77f6cSMauro Carvalho Chehab 		}
1118314527acSHans Verkuil 	} else if (i & V4L2_STD_625_50) {
111992cde477SDean Anderson 		dprintk(vc->dev, 4, "%s 50 Hz\n", __func__);
11200aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_PAL) {
11210aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
11220aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_PAL;
11230aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
11245e950fafSDean Anderson 			vc->width = LINE_SZ_4CIFS_PAL;
11255e950fafSDean Anderson 			vc->height = NUM_LINES_4CIFS_PAL * 2;
11260aa77f6cSMauro Carvalho Chehab 		}
1127340a30c5Ssensoray-dev 	} else
1128340a30c5Ssensoray-dev 		return -EINVAL;
112992cde477SDean Anderson 	vc->std = i;
11300aa77f6cSMauro Carvalho Chehab 	if (mode.restart)
113192cde477SDean Anderson 		s2255_set_mode(vc, &mode);
1132340a30c5Ssensoray-dev 	return 0;
11330aa77f6cSMauro Carvalho Chehab }
11340aa77f6cSMauro Carvalho Chehab 
vidioc_g_std(struct file * file,void * priv,v4l2_std_id * i)1135469af77aSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
1136469af77aSHans Verkuil {
1137340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
1138469af77aSHans Verkuil 
113992cde477SDean Anderson 	*i = vc->std;
1140469af77aSHans Verkuil 	return 0;
1141469af77aSHans Verkuil }
1142469af77aSHans Verkuil 
11430aa77f6cSMauro Carvalho Chehab /* Sensoray 2255 is a multiple channel capture device.
11440aa77f6cSMauro Carvalho Chehab    It does not have a "crossbar" of inputs.
11450aa77f6cSMauro Carvalho Chehab    We use one V4L device per channel. The user must
11460aa77f6cSMauro Carvalho Chehab    be aware that certain combinations are not allowed.
11470aa77f6cSMauro Carvalho Chehab    For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
11480aa77f6cSMauro Carvalho Chehab    at once in color(you can do full fps on 4 channels with greyscale.
11490aa77f6cSMauro Carvalho Chehab */
vidioc_enum_input(struct file * file,void * priv,struct v4l2_input * inp)11500aa77f6cSMauro Carvalho Chehab static int vidioc_enum_input(struct file *file, void *priv,
11510aa77f6cSMauro Carvalho Chehab 			     struct v4l2_input *inp)
11520aa77f6cSMauro Carvalho Chehab {
1153340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
115492cde477SDean Anderson 	struct s2255_dev *dev = vc->dev;
11550aa77f6cSMauro Carvalho Chehab 	u32 status = 0;
115692cde477SDean Anderson 
11570aa77f6cSMauro Carvalho Chehab 	if (inp->index != 0)
11580aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
11590aa77f6cSMauro Carvalho Chehab 	inp->type = V4L2_INPUT_TYPE_CAMERA;
11600aa77f6cSMauro Carvalho Chehab 	inp->std = S2255_NORMS;
11610aa77f6cSMauro Carvalho Chehab 	inp->status = 0;
11620aa77f6cSMauro Carvalho Chehab 	if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
11630aa77f6cSMauro Carvalho Chehab 		int rc;
116492cde477SDean Anderson 		rc = s2255_cmd_status(vc, &status);
1165f5402007Ssensoray-dev 		dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n",
1166f5402007Ssensoray-dev 			rc, status);
11670aa77f6cSMauro Carvalho Chehab 		if (rc == 0)
11680aa77f6cSMauro Carvalho Chehab 			inp->status =  (status & 0x01) ? 0
11690aa77f6cSMauro Carvalho Chehab 				: V4L2_IN_ST_NO_SIGNAL;
11700aa77f6cSMauro Carvalho Chehab 	}
11710aa77f6cSMauro Carvalho Chehab 	switch (dev->pid) {
11720aa77f6cSMauro Carvalho Chehab 	case 0x2255:
11730aa77f6cSMauro Carvalho Chehab 	default:
1174c0decac1SMauro Carvalho Chehab 		strscpy(inp->name, "Composite", sizeof(inp->name));
11750aa77f6cSMauro Carvalho Chehab 		break;
11760aa77f6cSMauro Carvalho Chehab 	case 0x2257:
1177c0decac1SMauro Carvalho Chehab 		strscpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video",
11780aa77f6cSMauro Carvalho Chehab 			sizeof(inp->name));
11790aa77f6cSMauro Carvalho Chehab 		break;
11800aa77f6cSMauro Carvalho Chehab 	}
11810aa77f6cSMauro Carvalho Chehab 	return 0;
11820aa77f6cSMauro Carvalho Chehab }
11830aa77f6cSMauro Carvalho Chehab 
vidioc_g_input(struct file * file,void * priv,unsigned int * i)11840aa77f6cSMauro Carvalho Chehab static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
11850aa77f6cSMauro Carvalho Chehab {
11860aa77f6cSMauro Carvalho Chehab 	*i = 0;
11870aa77f6cSMauro Carvalho Chehab 	return 0;
11880aa77f6cSMauro Carvalho Chehab }
vidioc_s_input(struct file * file,void * priv,unsigned int i)11890aa77f6cSMauro Carvalho Chehab static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
11900aa77f6cSMauro Carvalho Chehab {
11910aa77f6cSMauro Carvalho Chehab 	if (i > 0)
11920aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
11930aa77f6cSMauro Carvalho Chehab 	return 0;
11940aa77f6cSMauro Carvalho Chehab }
11950aa77f6cSMauro Carvalho Chehab 
s2255_s_ctrl(struct v4l2_ctrl * ctrl)1196192f1e78SHans Verkuil static int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
11970aa77f6cSMauro Carvalho Chehab {
11985e950fafSDean Anderson 	struct s2255_vc *vc =
11995e950fafSDean Anderson 		container_of(ctrl->handler, struct s2255_vc, hdl);
12000aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
12015e950fafSDean Anderson 	mode = vc->mode;
12020aa77f6cSMauro Carvalho Chehab 	/* update the mode to the corresponding value */
12030aa77f6cSMauro Carvalho Chehab 	switch (ctrl->id) {
12040aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1205192f1e78SHans Verkuil 		mode.bright = ctrl->val;
12060aa77f6cSMauro Carvalho Chehab 		break;
12070aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1208192f1e78SHans Verkuil 		mode.contrast = ctrl->val;
12090aa77f6cSMauro Carvalho Chehab 		break;
12100aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_HUE:
1211192f1e78SHans Verkuil 		mode.hue = ctrl->val;
12120aa77f6cSMauro Carvalho Chehab 		break;
12130aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1214192f1e78SHans Verkuil 		mode.saturation = ctrl->val;
12150aa77f6cSMauro Carvalho Chehab 		break;
1216192f1e78SHans Verkuil 	case V4L2_CID_S2255_COLORFILTER:
12170aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_INPUT_TYPE;
1218192f1e78SHans Verkuil 		mode.color |= !ctrl->val << 16;
12190aa77f6cSMauro Carvalho Chehab 		break;
12207041dec7SHans Verkuil 	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
12215e950fafSDean Anderson 		vc->jpegqual = ctrl->val;
12227041dec7SHans Verkuil 		return 0;
12230aa77f6cSMauro Carvalho Chehab 	default:
12240aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12250aa77f6cSMauro Carvalho Chehab 	}
12260aa77f6cSMauro Carvalho Chehab 	mode.restart = 0;
12270aa77f6cSMauro Carvalho Chehab 	/* set mode here.  Note: stream does not need restarted.
12280aa77f6cSMauro Carvalho Chehab 	   some V4L programs restart stream unnecessarily
12290aa77f6cSMauro Carvalho Chehab 	   after a s_crtl.
12300aa77f6cSMauro Carvalho Chehab 	*/
12315e950fafSDean Anderson 	s2255_set_mode(vc, &mode);
12320aa77f6cSMauro Carvalho Chehab 	return 0;
12330aa77f6cSMauro Carvalho Chehab }
12340aa77f6cSMauro Carvalho Chehab 
vidioc_g_jpegcomp(struct file * file,void * priv,struct v4l2_jpegcompression * jc)12350aa77f6cSMauro Carvalho Chehab static int vidioc_g_jpegcomp(struct file *file, void *priv,
12360aa77f6cSMauro Carvalho Chehab 			 struct v4l2_jpegcompression *jc)
12370aa77f6cSMauro Carvalho Chehab {
1238340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
12397041dec7SHans Verkuil 
12407041dec7SHans Verkuil 	memset(jc, 0, sizeof(*jc));
12415e950fafSDean Anderson 	jc->quality = vc->jpegqual;
124292cde477SDean Anderson 	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
12430aa77f6cSMauro Carvalho Chehab 	return 0;
12440aa77f6cSMauro Carvalho Chehab }
12450aa77f6cSMauro Carvalho Chehab 
vidioc_s_jpegcomp(struct file * file,void * priv,const struct v4l2_jpegcompression * jc)12460aa77f6cSMauro Carvalho Chehab static int vidioc_s_jpegcomp(struct file *file, void *priv,
1247d88aab53SHans Verkuil 			 const struct v4l2_jpegcompression *jc)
12480aa77f6cSMauro Carvalho Chehab {
1249340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
1250340a30c5Ssensoray-dev 
12510aa77f6cSMauro Carvalho Chehab 	if (jc->quality < 0 || jc->quality > 100)
12520aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12535e950fafSDean Anderson 	v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality);
125492cde477SDean Anderson 	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
12550aa77f6cSMauro Carvalho Chehab 	return 0;
12560aa77f6cSMauro Carvalho Chehab }
12570aa77f6cSMauro Carvalho Chehab 
vidioc_g_parm(struct file * file,void * priv,struct v4l2_streamparm * sp)12580aa77f6cSMauro Carvalho Chehab static int vidioc_g_parm(struct file *file, void *priv,
12590aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
12600aa77f6cSMauro Carvalho Chehab {
12610aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
1262340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
1263340a30c5Ssensoray-dev 
12640aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
12650aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12660aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
12675e950fafSDean Anderson 	sp->parm.capture.capturemode = vc->cap_parm.capturemode;
1268340a30c5Ssensoray-dev 	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
12695e950fafSDean Anderson 	def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000;
12705e950fafSDean Anderson 	def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000;
12710aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
12725e950fafSDean Anderson 	switch (vc->mode.fdec) {
12730aa77f6cSMauro Carvalho Chehab 	default:
12740aa77f6cSMauro Carvalho Chehab 	case FDEC_1:
12750aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
12760aa77f6cSMauro Carvalho Chehab 		break;
12770aa77f6cSMauro Carvalho Chehab 	case FDEC_2:
12780aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
12790aa77f6cSMauro Carvalho Chehab 		break;
12800aa77f6cSMauro Carvalho Chehab 	case FDEC_3:
12810aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
12820aa77f6cSMauro Carvalho Chehab 		break;
12830aa77f6cSMauro Carvalho Chehab 	case FDEC_5:
12840aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
12850aa77f6cSMauro Carvalho Chehab 		break;
12860aa77f6cSMauro Carvalho Chehab 	}
128792cde477SDean Anderson 	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n",
1288f5402007Ssensoray-dev 		__func__,
12890aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
12900aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
12910aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator);
12920aa77f6cSMauro Carvalho Chehab 	return 0;
12930aa77f6cSMauro Carvalho Chehab }
12940aa77f6cSMauro Carvalho Chehab 
vidioc_s_parm(struct file * file,void * priv,struct v4l2_streamparm * sp)12950aa77f6cSMauro Carvalho Chehab static int vidioc_s_parm(struct file *file, void *priv,
12960aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
12970aa77f6cSMauro Carvalho Chehab {
1298340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
12990aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
13000aa77f6cSMauro Carvalho Chehab 	int fdec = FDEC_1;
13010aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
13020aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
13030aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13045e950fafSDean Anderson 	mode = vc->mode;
13050aa77f6cSMauro Carvalho Chehab 	/* high quality capture mode requires a stream restart */
1306340a30c5Ssensoray-dev 	if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode)
1307340a30c5Ssensoray-dev 	    && vb2_is_streaming(&vc->vb_vidq))
13080aa77f6cSMauro Carvalho Chehab 		return -EBUSY;
13090aa77f6cSMauro Carvalho Chehab 	def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
13100aa77f6cSMauro Carvalho Chehab 	def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
13110aa77f6cSMauro Carvalho Chehab 	if (def_dem != sp->parm.capture.timeperframe.denominator)
13120aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
13130aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= def_num)
13140aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
13150aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
13160aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
13170aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_2;
13180aa77f6cSMauro Carvalho Chehab 	} else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
13190aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
13200aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_3;
13210aa77f6cSMauro Carvalho Chehab 	} else {
13220aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
13230aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_5;
13240aa77f6cSMauro Carvalho Chehab 	}
13250aa77f6cSMauro Carvalho Chehab 	mode.fdec = fdec;
13260aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
1327340a30c5Ssensoray-dev 	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
13285e950fafSDean Anderson 	s2255_set_mode(vc, &mode);
132992cde477SDean Anderson 	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
13300aa77f6cSMauro Carvalho Chehab 		__func__,
13310aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
13320aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
13330aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator, fdec);
13340aa77f6cSMauro Carvalho Chehab 	return 0;
13350aa77f6cSMauro Carvalho Chehab }
13360aa77f6cSMauro Carvalho Chehab 
133705e5d44bSHans Verkuil #define NUM_SIZE_ENUMS 3
133805e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete ntsc_sizes[] = {
133905e5d44bSHans Verkuil 	{ 640, 480 },
134005e5d44bSHans Verkuil 	{ 640, 240 },
134105e5d44bSHans Verkuil 	{ 320, 240 },
134205e5d44bSHans Verkuil };
134305e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete pal_sizes[] = {
134405e5d44bSHans Verkuil 	{ 704, 576 },
134505e5d44bSHans Verkuil 	{ 704, 288 },
134605e5d44bSHans Verkuil 	{ 352, 288 },
134705e5d44bSHans Verkuil };
134805e5d44bSHans Verkuil 
vidioc_enum_framesizes(struct file * file,void * priv,struct v4l2_frmsizeenum * fe)134905e5d44bSHans Verkuil static int vidioc_enum_framesizes(struct file *file, void *priv,
135005e5d44bSHans Verkuil 			    struct v4l2_frmsizeenum *fe)
135105e5d44bSHans Verkuil {
1352340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
13535e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
135405e5d44bSHans Verkuil 	const struct s2255_fmt *fmt;
135505e5d44bSHans Verkuil 
135605e5d44bSHans Verkuil 	if (fe->index >= NUM_SIZE_ENUMS)
135705e5d44bSHans Verkuil 		return -EINVAL;
135805e5d44bSHans Verkuil 
135905e5d44bSHans Verkuil 	fmt = format_by_fourcc(fe->pixel_format);
136005e5d44bSHans Verkuil 	if (fmt == NULL)
136105e5d44bSHans Verkuil 		return -EINVAL;
136205e5d44bSHans Verkuil 	fe->type = V4L2_FRMSIZE_TYPE_DISCRETE;
136305e5d44bSHans Verkuil 	fe->discrete = is_ntsc ?  ntsc_sizes[fe->index] : pal_sizes[fe->index];
136405e5d44bSHans Verkuil 	return 0;
136505e5d44bSHans Verkuil }
136605e5d44bSHans Verkuil 
vidioc_enum_frameintervals(struct file * file,void * priv,struct v4l2_frmivalenum * fe)13670aa77f6cSMauro Carvalho Chehab static int vidioc_enum_frameintervals(struct file *file, void *priv,
13680aa77f6cSMauro Carvalho Chehab 			    struct v4l2_frmivalenum *fe)
13690aa77f6cSMauro Carvalho Chehab {
1370340a30c5Ssensoray-dev 	struct s2255_vc *vc = video_drvdata(file);
137105e5d44bSHans Verkuil 	const struct s2255_fmt *fmt;
137205e5d44bSHans Verkuil 	const struct v4l2_frmsize_discrete *sizes;
13735e950fafSDean Anderson 	int is_ntsc = vc->std & V4L2_STD_525_60;
13740aa77f6cSMauro Carvalho Chehab #define NUM_FRAME_ENUMS 4
13750aa77f6cSMauro Carvalho Chehab 	int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
137605e5d44bSHans Verkuil 	int i;
137705e5d44bSHans Verkuil 
1378199ab8feSMauro Carvalho Chehab 	if (fe->index >= NUM_FRAME_ENUMS)
13790aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
138005e5d44bSHans Verkuil 
138105e5d44bSHans Verkuil 	fmt = format_by_fourcc(fe->pixel_format);
138205e5d44bSHans Verkuil 	if (fmt == NULL)
13830aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
138405e5d44bSHans Verkuil 
138505e5d44bSHans Verkuil 	sizes = is_ntsc ? ntsc_sizes : pal_sizes;
138605e5d44bSHans Verkuil 	for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++)
138705e5d44bSHans Verkuil 		if (fe->width == sizes->width &&
138805e5d44bSHans Verkuil 		    fe->height == sizes->height)
13890aa77f6cSMauro Carvalho Chehab 			break;
139005e5d44bSHans Verkuil 	if (i == NUM_SIZE_ENUMS)
13910aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
139205e5d44bSHans Verkuil 
13930aa77f6cSMauro Carvalho Chehab 	fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
13940aa77f6cSMauro Carvalho Chehab 	fe->discrete.denominator = is_ntsc ? 30000 : 25000;
13950aa77f6cSMauro Carvalho Chehab 	fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
139692cde477SDean Anderson 	dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__,
1397f5402007Ssensoray-dev 		fe->discrete.numerator,
13980aa77f6cSMauro Carvalho Chehab 		fe->discrete.denominator);
13990aa77f6cSMauro Carvalho Chehab 	return 0;
14000aa77f6cSMauro Carvalho Chehab }
14010aa77f6cSMauro Carvalho Chehab 
s2255_open(struct file * file)1402340a30c5Ssensoray-dev static int s2255_open(struct file *file)
14030aa77f6cSMauro Carvalho Chehab {
14045e950fafSDean Anderson 	struct s2255_vc *vc = video_drvdata(file);
1405340a30c5Ssensoray-dev 	struct s2255_dev *dev = vc->dev;
14060aa77f6cSMauro Carvalho Chehab 	int state;
1407340a30c5Ssensoray-dev 	int rc = 0;
1408340a30c5Ssensoray-dev 
1409340a30c5Ssensoray-dev 	rc = v4l2_fh_open(file);
1410340a30c5Ssensoray-dev 	if (rc != 0)
1411340a30c5Ssensoray-dev 		return rc;
1412340a30c5Ssensoray-dev 
1413340a30c5Ssensoray-dev 	dprintk(dev, 1, "s2255: %s\n", __func__);
14140aa77f6cSMauro Carvalho Chehab 	state = atomic_read(&dev->fw_data->fw_state);
14150aa77f6cSMauro Carvalho Chehab 	switch (state) {
14160aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
14170aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
14180aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
14190aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&dev->udev->dev,
14200aa77f6cSMauro Carvalho Chehab 			"firmware load failed. retrying.\n");
1421e2a06704SDean A 		s2255_fwload_start(dev);
14220aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
14230aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
14240aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
14250aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
14260aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
14270aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
14280aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
14290aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
14300aa77f6cSMauro Carvalho Chehab 		break;
14310aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
14320aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
14330aa77f6cSMauro Carvalho Chehab 		/* give S2255_LOAD_TIMEOUT time for firmware to load in case
14340aa77f6cSMauro Carvalho Chehab 		   driver loaded and then device immediately opened */
1435f5402007Ssensoray-dev 		pr_info("%s waiting for firmware load\n", __func__);
14360aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
14370aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
14380aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
14390aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
14400aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
14410aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
14420aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
14430aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
14440aa77f6cSMauro Carvalho Chehab 		break;
14450aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
14460aa77f6cSMauro Carvalho Chehab 	default:
14470aa77f6cSMauro Carvalho Chehab 		break;
14480aa77f6cSMauro Carvalho Chehab 	}
14490aa77f6cSMauro Carvalho Chehab 	/* state may have changed in above switch statement */
14500aa77f6cSMauro Carvalho Chehab 	switch (state) {
14510aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
14520aa77f6cSMauro Carvalho Chehab 		break;
14530aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
1454f5402007Ssensoray-dev 		pr_info("2255 firmware load failed.\n");
14550aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
14560aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
1457f5402007Ssensoray-dev 		pr_info("%s: disconnecting\n", __func__);
14580aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
14590aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
14600aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
1461f5402007Ssensoray-dev 		pr_info("%s: firmware not loaded, please retry\n",
14620aa77f6cSMauro Carvalho Chehab 			__func__);
14630aa77f6cSMauro Carvalho Chehab 		/*
14640aa77f6cSMauro Carvalho Chehab 		 * Timeout on firmware load means device unusable.
14650aa77f6cSMauro Carvalho Chehab 		 * Set firmware failure state.
14660aa77f6cSMauro Carvalho Chehab 		 * On next s2255_open the firmware will be reloaded.
14670aa77f6cSMauro Carvalho Chehab 		 */
14680aa77f6cSMauro Carvalho Chehab 		atomic_set(&dev->fw_data->fw_state,
14690aa77f6cSMauro Carvalho Chehab 			   S2255_FW_FAILED);
14700aa77f6cSMauro Carvalho Chehab 		return -EAGAIN;
14710aa77f6cSMauro Carvalho Chehab 	default:
1472f5402007Ssensoray-dev 		pr_info("%s: unknown state\n", __func__);
14730aa77f6cSMauro Carvalho Chehab 		return -EFAULT;
14740aa77f6cSMauro Carvalho Chehab 	}
14755e950fafSDean Anderson 	if (!vc->configured) {
14760aa77f6cSMauro Carvalho Chehab 		/* configure channel to default state */
14775e950fafSDean Anderson 		vc->fmt = &formats[0];
14785e950fafSDean Anderson 		s2255_set_mode(vc, &vc->mode);
14795e950fafSDean Anderson 		vc->configured = 1;
14800aa77f6cSMauro Carvalho Chehab 	}
14810aa77f6cSMauro Carvalho Chehab 	return 0;
14820aa77f6cSMauro Carvalho Chehab }
14830aa77f6cSMauro Carvalho Chehab 
s2255_destroy(struct s2255_dev * dev)14840aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev)
14850aa77f6cSMauro Carvalho Chehab {
1486f5402007Ssensoray-dev 	dprintk(dev, 1, "%s", __func__);
14870aa77f6cSMauro Carvalho Chehab 	/* board shutdown stops the read pipe if it is running */
14880aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
14890aa77f6cSMauro Carvalho Chehab 	/* make sure firmware still not trying to load */
1490292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&dev->timer);  /* only started in .probe and .open */
14910aa77f6cSMauro Carvalho Chehab 	if (dev->fw_data->fw_urb) {
14920aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(dev->fw_data->fw_urb);
14930aa77f6cSMauro Carvalho Chehab 		usb_free_urb(dev->fw_data->fw_urb);
14940aa77f6cSMauro Carvalho Chehab 		dev->fw_data->fw_urb = NULL;
14950aa77f6cSMauro Carvalho Chehab 	}
14960aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
14970aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
14980aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
14990aa77f6cSMauro Carvalho Chehab 	/* reset the DSP so firmware can be reloaded next time */
15000aa77f6cSMauro Carvalho Chehab 	s2255_reset_dsppower(dev);
15010aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
15020aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
15030aa77f6cSMauro Carvalho Chehab 	v4l2_device_unregister(&dev->v4l2_dev);
150447d8c881SDean Anderson 	kfree(dev->cmdbuf);
15050aa77f6cSMauro Carvalho Chehab 	kfree(dev);
15060aa77f6cSMauro Carvalho Chehab }
15070aa77f6cSMauro Carvalho Chehab 
15080aa77f6cSMauro Carvalho Chehab static const struct v4l2_file_operations s2255_fops_v4l = {
15090aa77f6cSMauro Carvalho Chehab 	.owner = THIS_MODULE,
15100aa77f6cSMauro Carvalho Chehab 	.open = s2255_open,
1511340a30c5Ssensoray-dev 	.release = vb2_fop_release,
1512340a30c5Ssensoray-dev 	.poll = vb2_fop_poll,
15130aa77f6cSMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,	/* V4L2 ioctl handler */
1514340a30c5Ssensoray-dev 	.mmap = vb2_fop_mmap,
1515340a30c5Ssensoray-dev 	.read = vb2_fop_read,
15160aa77f6cSMauro Carvalho Chehab };
15170aa77f6cSMauro Carvalho Chehab 
15180aa77f6cSMauro Carvalho Chehab static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
15190aa77f6cSMauro Carvalho Chehab 	.vidioc_querycap = vidioc_querycap,
15200aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
15210aa77f6cSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
15220aa77f6cSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
15230aa77f6cSMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
1524340a30c5Ssensoray-dev 	.vidioc_reqbufs = vb2_ioctl_reqbufs,
1525340a30c5Ssensoray-dev 	.vidioc_querybuf = vb2_ioctl_querybuf,
1526340a30c5Ssensoray-dev 	.vidioc_qbuf = vb2_ioctl_qbuf,
1527340a30c5Ssensoray-dev 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
15280aa77f6cSMauro Carvalho Chehab 	.vidioc_s_std = vidioc_s_std,
1529469af77aSHans Verkuil 	.vidioc_g_std = vidioc_g_std,
15300aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_input = vidioc_enum_input,
15310aa77f6cSMauro Carvalho Chehab 	.vidioc_g_input = vidioc_g_input,
15320aa77f6cSMauro Carvalho Chehab 	.vidioc_s_input = vidioc_s_input,
1533340a30c5Ssensoray-dev 	.vidioc_streamon = vb2_ioctl_streamon,
1534340a30c5Ssensoray-dev 	.vidioc_streamoff = vb2_ioctl_streamoff,
15350aa77f6cSMauro Carvalho Chehab 	.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
15360aa77f6cSMauro Carvalho Chehab 	.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
15370aa77f6cSMauro Carvalho Chehab 	.vidioc_s_parm = vidioc_s_parm,
15380aa77f6cSMauro Carvalho Chehab 	.vidioc_g_parm = vidioc_g_parm,
153905e5d44bSHans Verkuil 	.vidioc_enum_framesizes = vidioc_enum_framesizes,
15400aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
154144d06d82SHans Verkuil 	.vidioc_log_status  = v4l2_ctrl_log_status,
154244d06d82SHans Verkuil 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
154344d06d82SHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
15440aa77f6cSMauro Carvalho Chehab };
15450aa77f6cSMauro Carvalho Chehab 
s2255_video_device_release(struct video_device * vdev)15460aa77f6cSMauro Carvalho Chehab static void s2255_video_device_release(struct video_device *vdev)
15470aa77f6cSMauro Carvalho Chehab {
15480aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
15495e950fafSDean Anderson 	struct s2255_vc *vc =
15505e950fafSDean Anderson 		container_of(vdev, struct s2255_vc, vdev);
1551192f1e78SHans Verkuil 
1552f5402007Ssensoray-dev 	dprintk(dev, 4, "%s, chnls: %d\n", __func__,
1553*29ce81f9SRicardo Ribalda 		refcount_read(&dev->num_channels));
1554192f1e78SHans Verkuil 
15555e950fafSDean Anderson 	v4l2_ctrl_handler_free(&vc->hdl);
1556f5402007Ssensoray-dev 
1557*29ce81f9SRicardo Ribalda 	if (refcount_dec_and_test(&dev->num_channels))
15580aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
15590aa77f6cSMauro Carvalho Chehab 	return;
15600aa77f6cSMauro Carvalho Chehab }
15610aa77f6cSMauro Carvalho Chehab 
156286844942SBhumika Goyal static const struct video_device template = {
15630aa77f6cSMauro Carvalho Chehab 	.name = "s2255v",
15640aa77f6cSMauro Carvalho Chehab 	.fops = &s2255_fops_v4l,
15650aa77f6cSMauro Carvalho Chehab 	.ioctl_ops = &s2255_ioctl_ops,
15660aa77f6cSMauro Carvalho Chehab 	.release = s2255_video_device_release,
15670aa77f6cSMauro Carvalho Chehab 	.tvnorms = S2255_NORMS,
15680aa77f6cSMauro Carvalho Chehab };
15690aa77f6cSMauro Carvalho Chehab 
1570192f1e78SHans Verkuil static const struct v4l2_ctrl_ops s2255_ctrl_ops = {
1571192f1e78SHans Verkuil 	.s_ctrl = s2255_s_ctrl,
1572192f1e78SHans Verkuil };
1573192f1e78SHans Verkuil 
1574192f1e78SHans Verkuil static const struct v4l2_ctrl_config color_filter_ctrl = {
1575192f1e78SHans Verkuil 	.ops = &s2255_ctrl_ops,
1576192f1e78SHans Verkuil 	.name = "Color Filter",
1577192f1e78SHans Verkuil 	.id = V4L2_CID_S2255_COLORFILTER,
1578192f1e78SHans Verkuil 	.type = V4L2_CTRL_TYPE_BOOLEAN,
1579192f1e78SHans Verkuil 	.max = 1,
1580192f1e78SHans Verkuil 	.step = 1,
1581192f1e78SHans Verkuil 	.def = 1,
1582192f1e78SHans Verkuil };
1583192f1e78SHans Verkuil 
s2255_probe_v4l(struct s2255_dev * dev)15840aa77f6cSMauro Carvalho Chehab static int s2255_probe_v4l(struct s2255_dev *dev)
15850aa77f6cSMauro Carvalho Chehab {
15860aa77f6cSMauro Carvalho Chehab 	int ret;
15870aa77f6cSMauro Carvalho Chehab 	int i;
15880aa77f6cSMauro Carvalho Chehab 	int cur_nr = video_nr;
15895e950fafSDean Anderson 	struct s2255_vc *vc;
1590340a30c5Ssensoray-dev 	struct vb2_queue *q;
1591340a30c5Ssensoray-dev 
15920aa77f6cSMauro Carvalho Chehab 	ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
15930aa77f6cSMauro Carvalho Chehab 	if (ret)
15940aa77f6cSMauro Carvalho Chehab 		return ret;
15950aa77f6cSMauro Carvalho Chehab 	/* initialize all video 4 linux */
15960aa77f6cSMauro Carvalho Chehab 	/* register 4 video devices */
15970aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
15985e950fafSDean Anderson 		vc = &dev->vc[i];
15995e950fafSDean Anderson 		INIT_LIST_HEAD(&vc->buf_list);
1600192f1e78SHans Verkuil 
16015e950fafSDean Anderson 		v4l2_ctrl_handler_init(&vc->hdl, 6);
16025e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1603192f1e78SHans Verkuil 				V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT);
16045e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1605192f1e78SHans Verkuil 				V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST);
16065e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1607192f1e78SHans Verkuil 				V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION);
16085e950fafSDean Anderson 		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
1609192f1e78SHans Verkuil 				V4L2_CID_HUE, 0, 255, 1, DEF_HUE);
16105e950fafSDean Anderson 		vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl,
16117041dec7SHans Verkuil 				&s2255_ctrl_ops,
16127041dec7SHans Verkuil 				V4L2_CID_JPEG_COMPRESSION_QUALITY,
16137041dec7SHans Verkuil 				0, 100, 1, S2255_DEF_JPEG_QUAL);
1614192f1e78SHans Verkuil 		if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER &&
16155e950fafSDean Anderson 		    (dev->pid != 0x2257 || vc->idx <= 1))
16165e950fafSDean Anderson 			v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl,
1617f5402007Ssensoray-dev 					     NULL);
16185e950fafSDean Anderson 		if (vc->hdl.error) {
16195e950fafSDean Anderson 			ret = vc->hdl.error;
16205e950fafSDean Anderson 			v4l2_ctrl_handler_free(&vc->hdl);
1621192f1e78SHans Verkuil 			dev_err(&dev->udev->dev, "couldn't register control\n");
1622192f1e78SHans Verkuil 			break;
1623192f1e78SHans Verkuil 		}
1624340a30c5Ssensoray-dev 		q = &vc->vb_vidq;
1625340a30c5Ssensoray-dev 		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1626340a30c5Ssensoray-dev 		q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR;
1627340a30c5Ssensoray-dev 		q->drv_priv = vc;
1628340a30c5Ssensoray-dev 		q->lock = &vc->vb_lock;
1629340a30c5Ssensoray-dev 		q->buf_struct_size = sizeof(struct s2255_buffer);
1630340a30c5Ssensoray-dev 		q->mem_ops = &vb2_vmalloc_memops;
1631340a30c5Ssensoray-dev 		q->ops = &s2255_video_qops;
1632ade48681SSakari Ailus 		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1633340a30c5Ssensoray-dev 		ret = vb2_queue_init(q);
1634340a30c5Ssensoray-dev 		if (ret != 0) {
1635340a30c5Ssensoray-dev 			dev_err(&dev->udev->dev,
1636340a30c5Ssensoray-dev 				"%s vb2_queue_init 0x%x\n", __func__, ret);
1637340a30c5Ssensoray-dev 			break;
1638340a30c5Ssensoray-dev 		}
1639340a30c5Ssensoray-dev 		/* register video devices */
16405e950fafSDean Anderson 		vc->vdev = template;
1641340a30c5Ssensoray-dev 		vc->vdev.queue = q;
16425e950fafSDean Anderson 		vc->vdev.ctrl_handler = &vc->hdl;
16435e950fafSDean Anderson 		vc->vdev.lock = &dev->lock;
16445e950fafSDean Anderson 		vc->vdev.v4l2_dev = &dev->v4l2_dev;
16458c3854d0SHans Verkuil 		vc->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
16468c3854d0SHans Verkuil 				       V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
16475e950fafSDean Anderson 		video_set_drvdata(&vc->vdev, vc);
16480aa77f6cSMauro Carvalho Chehab 		if (video_nr == -1)
16495e950fafSDean Anderson 			ret = video_register_device(&vc->vdev,
16507fbbbc78SHans Verkuil 						    VFL_TYPE_VIDEO,
16510aa77f6cSMauro Carvalho Chehab 						    video_nr);
16520aa77f6cSMauro Carvalho Chehab 		else
16535e950fafSDean Anderson 			ret = video_register_device(&vc->vdev,
16547fbbbc78SHans Verkuil 						    VFL_TYPE_VIDEO,
16550aa77f6cSMauro Carvalho Chehab 						    cur_nr + i);
16560aa77f6cSMauro Carvalho Chehab 
16570aa77f6cSMauro Carvalho Chehab 		if (ret) {
16580aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev,
16590aa77f6cSMauro Carvalho Chehab 				"failed to register video device!\n");
16600aa77f6cSMauro Carvalho Chehab 			break;
16610aa77f6cSMauro Carvalho Chehab 		}
1662*29ce81f9SRicardo Ribalda 		refcount_inc(&dev->num_channels);
16630aa77f6cSMauro Carvalho Chehab 		v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
16645e950fafSDean Anderson 			  video_device_node_name(&vc->vdev));
16650aa77f6cSMauro Carvalho Chehab 
16660aa77f6cSMauro Carvalho Chehab 	}
1667f5402007Ssensoray-dev 	pr_info("Sensoray 2255 V4L driver Revision: %s\n",
16680aa77f6cSMauro Carvalho Chehab 		S2255_VERSION);
16690aa77f6cSMauro Carvalho Chehab 	/* if no channels registered, return error and probe will fail*/
1670*29ce81f9SRicardo Ribalda 	if (refcount_read(&dev->num_channels) == 0) {
16710aa77f6cSMauro Carvalho Chehab 		v4l2_device_unregister(&dev->v4l2_dev);
16720aa77f6cSMauro Carvalho Chehab 		return ret;
16730aa77f6cSMauro Carvalho Chehab 	}
1674*29ce81f9SRicardo Ribalda 	if (refcount_read(&dev->num_channels) != MAX_CHANNELS)
1675f5402007Ssensoray-dev 		pr_warn("s2255: Not all channels available.\n");
16760aa77f6cSMauro Carvalho Chehab 	return 0;
16770aa77f6cSMauro Carvalho Chehab }
16780aa77f6cSMauro Carvalho Chehab 
16790aa77f6cSMauro Carvalho Chehab /* this function moves the usb stream read pipe data
16800aa77f6cSMauro Carvalho Chehab  * into the system buffers.
16810aa77f6cSMauro Carvalho Chehab  * returns 0 on success, EAGAIN if more data to process( call this
16820aa77f6cSMauro Carvalho Chehab  * function again).
16830aa77f6cSMauro Carvalho Chehab  *
16840aa77f6cSMauro Carvalho Chehab  * Received frame structure:
16850aa77f6cSMauro Carvalho Chehab  * bytes 0-3:  marker : 0x2255DA4AL (S2255_MARKER_FRAME)
16860aa77f6cSMauro Carvalho Chehab  * bytes 4-7:  channel: 0-3
16870aa77f6cSMauro Carvalho Chehab  * bytes 8-11: payload size:  size of the frame
16880aa77f6cSMauro Carvalho Chehab  * bytes 12-payloadsize+12:  frame data
16890aa77f6cSMauro Carvalho Chehab  */
save_frame(struct s2255_dev * dev,struct s2255_pipeinfo * pipe_info)16900aa77f6cSMauro Carvalho Chehab static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
16910aa77f6cSMauro Carvalho Chehab {
16920aa77f6cSMauro Carvalho Chehab 	char *pdest;
16930aa77f6cSMauro Carvalho Chehab 	u32 offset = 0;
16940aa77f6cSMauro Carvalho Chehab 	int bframe = 0;
16950aa77f6cSMauro Carvalho Chehab 	char *psrc;
16960aa77f6cSMauro Carvalho Chehab 	unsigned long copy_size;
16970aa77f6cSMauro Carvalho Chehab 	unsigned long size;
16980aa77f6cSMauro Carvalho Chehab 	s32 idx = -1;
16990aa77f6cSMauro Carvalho Chehab 	struct s2255_framei *frm;
17000aa77f6cSMauro Carvalho Chehab 	unsigned char *pdata;
17015e950fafSDean Anderson 	struct s2255_vc *vc;
1702f5402007Ssensoray-dev 	dprintk(dev, 100, "buffer to user\n");
17035e950fafSDean Anderson 	vc = &dev->vc[dev->cc];
17045e950fafSDean Anderson 	idx = vc->cur_frame;
17055e950fafSDean Anderson 	frm = &vc->buffer.frame[idx];
17060aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
17070aa77f6cSMauro Carvalho Chehab 		int jj;
17080aa77f6cSMauro Carvalho Chehab 		unsigned int cc;
17090aa77f6cSMauro Carvalho Chehab 		__le32 *pdword; /*data from dsp is little endian */
17100aa77f6cSMauro Carvalho Chehab 		int payload;
17110aa77f6cSMauro Carvalho Chehab 		/* search for marker codes */
17120aa77f6cSMauro Carvalho Chehab 		pdata = (unsigned char *)pipe_info->transfer_buffer;
17130aa77f6cSMauro Carvalho Chehab 		pdword = (__le32 *)pdata;
17140aa77f6cSMauro Carvalho Chehab 		for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
17150aa77f6cSMauro Carvalho Chehab 			switch (*pdword) {
17160aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_FRAME:
1717f5402007Ssensoray-dev 				dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
1718f5402007Ssensoray-dev 					jj, pdata[0], pdata[1]);
17190aa77f6cSMauro Carvalho Chehab 				offset = jj + PREFIX_SIZE;
17200aa77f6cSMauro Carvalho Chehab 				bframe = 1;
17210aa77f6cSMauro Carvalho Chehab 				cc = le32_to_cpu(pdword[1]);
17220aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS) {
1723f5402007Ssensoray-dev 					dprintk(dev, 0,
17240aa77f6cSMauro Carvalho Chehab 						"bad channel\n");
17250aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
17260aa77f6cSMauro Carvalho Chehab 				}
17270aa77f6cSMauro Carvalho Chehab 				/* reverse it */
17280aa77f6cSMauro Carvalho Chehab 				dev->cc = G_chnmap[cc];
17295e950fafSDean Anderson 				vc = &dev->vc[dev->cc];
17300aa77f6cSMauro Carvalho Chehab 				payload =  le32_to_cpu(pdword[3]);
17315e950fafSDean Anderson 				if (payload > vc->req_image_size) {
17325e950fafSDean Anderson 					vc->bad_payload++;
17330aa77f6cSMauro Carvalho Chehab 					/* discard the bad frame */
17340aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
17350aa77f6cSMauro Carvalho Chehab 				}
17365e950fafSDean Anderson 				vc->pkt_size = payload;
17375e950fafSDean Anderson 				vc->jpg_size = le32_to_cpu(pdword[4]);
17380aa77f6cSMauro Carvalho Chehab 				break;
17390aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_RESPONSE:
17400aa77f6cSMauro Carvalho Chehab 
17410aa77f6cSMauro Carvalho Chehab 				pdata += DEF_USB_BLOCK;
17420aa77f6cSMauro Carvalho Chehab 				jj += DEF_USB_BLOCK;
17430aa77f6cSMauro Carvalho Chehab 				if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
17440aa77f6cSMauro Carvalho Chehab 					break;
17450aa77f6cSMauro Carvalho Chehab 				cc = G_chnmap[le32_to_cpu(pdword[1])];
17460aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS)
17470aa77f6cSMauro Carvalho Chehab 					break;
17485e950fafSDean Anderson 				vc = &dev->vc[cc];
17490aa77f6cSMauro Carvalho Chehab 				switch (pdword[2]) {
17500aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_SETMODE:
17510aa77f6cSMauro Carvalho Chehab 					/* check if channel valid */
17520aa77f6cSMauro Carvalho Chehab 					/* set mode ready */
17535e950fafSDean Anderson 					vc->setmode_ready = 1;
17545e950fafSDean Anderson 					wake_up(&vc->wait_setmode);
1755f5402007Ssensoray-dev 					dprintk(dev, 5, "setmode rdy %d\n", cc);
17560aa77f6cSMauro Carvalho Chehab 					break;
17570aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_FW:
17580aa77f6cSMauro Carvalho Chehab 					dev->chn_ready |= (1 << cc);
17590aa77f6cSMauro Carvalho Chehab 					if ((dev->chn_ready & 0x0f) != 0x0f)
17600aa77f6cSMauro Carvalho Chehab 						break;
17610aa77f6cSMauro Carvalho Chehab 					/* all channels ready */
1762f5402007Ssensoray-dev 					pr_info("s2255: fw loaded\n");
17630aa77f6cSMauro Carvalho Chehab 					atomic_set(&dev->fw_data->fw_state,
17640aa77f6cSMauro Carvalho Chehab 						   S2255_FW_SUCCESS);
17650aa77f6cSMauro Carvalho Chehab 					wake_up(&dev->fw_data->wait_fw);
17660aa77f6cSMauro Carvalho Chehab 					break;
17670aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_STATUS:
17685e950fafSDean Anderson 					vc->vidstatus = le32_to_cpu(pdword[3]);
17695e950fafSDean Anderson 					vc->vidstatus_ready = 1;
17705e950fafSDean Anderson 					wake_up(&vc->wait_vidstatus);
1771f5402007Ssensoray-dev 					dprintk(dev, 5, "vstat %x chan %d\n",
17720aa77f6cSMauro Carvalho Chehab 						le32_to_cpu(pdword[3]), cc);
17730aa77f6cSMauro Carvalho Chehab 					break;
17740aa77f6cSMauro Carvalho Chehab 				default:
1775f5402007Ssensoray-dev 					pr_info("s2255 unknown resp\n");
17760aa77f6cSMauro Carvalho Chehab 				}
1777ec33fbd5SMauro Carvalho Chehab 				pdata++;
1778ec33fbd5SMauro Carvalho Chehab 				break;
17790aa77f6cSMauro Carvalho Chehab 			default:
17800aa77f6cSMauro Carvalho Chehab 				pdata++;
17810aa77f6cSMauro Carvalho Chehab 				break;
17820aa77f6cSMauro Carvalho Chehab 			}
17830aa77f6cSMauro Carvalho Chehab 			if (bframe)
17840aa77f6cSMauro Carvalho Chehab 				break;
17850aa77f6cSMauro Carvalho Chehab 		} /* for */
17860aa77f6cSMauro Carvalho Chehab 		if (!bframe)
17870aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
17880aa77f6cSMauro Carvalho Chehab 	}
17895e950fafSDean Anderson 	vc = &dev->vc[dev->cc];
17905e950fafSDean Anderson 	idx = vc->cur_frame;
17915e950fafSDean Anderson 	frm = &vc->buffer.frame[idx];
17920aa77f6cSMauro Carvalho Chehab 	/* search done.  now find out if should be acquiring on this channel */
1793340a30c5Ssensoray-dev 	if (!vb2_is_streaming(&vc->vb_vidq)) {
17940aa77f6cSMauro Carvalho Chehab 		/* we found a frame, but this channel is turned off */
17950aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
17960aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
17970aa77f6cSMauro Carvalho Chehab 	}
17980aa77f6cSMauro Carvalho Chehab 
17990aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
18000aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_FRAME;
18010aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
18020aa77f6cSMauro Carvalho Chehab 	}
18030aa77f6cSMauro Carvalho Chehab 
18040aa77f6cSMauro Carvalho Chehab 	/* skip the marker 512 bytes (and offset if out of sync) */
18050aa77f6cSMauro Carvalho Chehab 	psrc = (u8 *)pipe_info->transfer_buffer + offset;
18060aa77f6cSMauro Carvalho Chehab 
18070aa77f6cSMauro Carvalho Chehab 
18080aa77f6cSMauro Carvalho Chehab 	if (frm->lpvbits == NULL) {
1809f5402007Ssensoray-dev 		dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d",
18100aa77f6cSMauro Carvalho Chehab 			frm, dev, dev->cc, idx);
18110aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
18120aa77f6cSMauro Carvalho Chehab 	}
18130aa77f6cSMauro Carvalho Chehab 
18140aa77f6cSMauro Carvalho Chehab 	pdest = frm->lpvbits + frm->cur_size;
18150aa77f6cSMauro Carvalho Chehab 
18160aa77f6cSMauro Carvalho Chehab 	copy_size = (pipe_info->cur_transfer_size - offset);
18170aa77f6cSMauro Carvalho Chehab 
18185e950fafSDean Anderson 	size = vc->pkt_size - PREFIX_SIZE;
18190aa77f6cSMauro Carvalho Chehab 
18200aa77f6cSMauro Carvalho Chehab 	/* sanity check on pdest */
18215e950fafSDean Anderson 	if ((copy_size + frm->cur_size) < vc->req_image_size)
18220aa77f6cSMauro Carvalho Chehab 		memcpy(pdest, psrc, copy_size);
18230aa77f6cSMauro Carvalho Chehab 
18240aa77f6cSMauro Carvalho Chehab 	frm->cur_size += copy_size;
1825f5402007Ssensoray-dev 	dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size);
18260aa77f6cSMauro Carvalho Chehab 
18270aa77f6cSMauro Carvalho Chehab 	if (frm->cur_size >= size) {
1828f5402007Ssensoray-dev 		dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n",
18290aa77f6cSMauro Carvalho Chehab 			dev->cc, idx);
18305e950fafSDean Anderson 		vc->last_frame = vc->cur_frame;
18315e950fafSDean Anderson 		vc->cur_frame++;
18320aa77f6cSMauro Carvalho Chehab 		/* end of system frame ring buffer, start at zero */
18335e950fafSDean Anderson 		if ((vc->cur_frame == SYS_FRAMES) ||
18345e950fafSDean Anderson 		    (vc->cur_frame == vc->buffer.dwFrames))
18355e950fafSDean Anderson 			vc->cur_frame = 0;
18360aa77f6cSMauro Carvalho Chehab 		/* frame ready */
1837340a30c5Ssensoray-dev 		if (vb2_is_streaming(&vc->vb_vidq))
18385e950fafSDean Anderson 			s2255_got_frame(vc, vc->jpg_size);
18395e950fafSDean Anderson 		vc->frame_count++;
18400aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
18410aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
18420aa77f6cSMauro Carvalho Chehab 
18430aa77f6cSMauro Carvalho Chehab 	}
18440aa77f6cSMauro Carvalho Chehab 	/* done successfully */
18450aa77f6cSMauro Carvalho Chehab 	return 0;
18460aa77f6cSMauro Carvalho Chehab }
18470aa77f6cSMauro Carvalho Chehab 
s2255_read_video_callback(struct s2255_dev * dev,struct s2255_pipeinfo * pipe_info)18480aa77f6cSMauro Carvalho Chehab static void s2255_read_video_callback(struct s2255_dev *dev,
18490aa77f6cSMauro Carvalho Chehab 				      struct s2255_pipeinfo *pipe_info)
18500aa77f6cSMauro Carvalho Chehab {
18510aa77f6cSMauro Carvalho Chehab 	int res;
1852f5402007Ssensoray-dev 	dprintk(dev, 50, "callback read video\n");
18530aa77f6cSMauro Carvalho Chehab 
18540aa77f6cSMauro Carvalho Chehab 	if (dev->cc >= MAX_CHANNELS) {
18550aa77f6cSMauro Carvalho Chehab 		dev->cc = 0;
18560aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid channel\n");
18570aa77f6cSMauro Carvalho Chehab 		return;
18580aa77f6cSMauro Carvalho Chehab 	}
18590aa77f6cSMauro Carvalho Chehab 	/* otherwise copy to the system buffers */
18600aa77f6cSMauro Carvalho Chehab 	res = save_frame(dev, pipe_info);
18610aa77f6cSMauro Carvalho Chehab 	if (res != 0)
1862f5402007Ssensoray-dev 		dprintk(dev, 4, "s2255: read callback failed\n");
18630aa77f6cSMauro Carvalho Chehab 
1864f5402007Ssensoray-dev 	dprintk(dev, 50, "callback read video done\n");
18650aa77f6cSMauro Carvalho Chehab 	return;
18660aa77f6cSMauro Carvalho Chehab }
18670aa77f6cSMauro Carvalho Chehab 
s2255_vendor_req(struct s2255_dev * dev,unsigned char Request,u16 Index,u16 Value,void * TransferBuffer,s32 TransferBufferLength,int bOut)18680aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
18690aa77f6cSMauro Carvalho Chehab 			     u16 Index, u16 Value, void *TransferBuffer,
18700aa77f6cSMauro Carvalho Chehab 			     s32 TransferBufferLength, int bOut)
18710aa77f6cSMauro Carvalho Chehab {
18720aa77f6cSMauro Carvalho Chehab 	int r;
1873db65c49eSMauro Carvalho Chehab 	unsigned char *buf;
1874db65c49eSMauro Carvalho Chehab 
1875db65c49eSMauro Carvalho Chehab 	buf = kmalloc(TransferBufferLength, GFP_KERNEL);
1876db65c49eSMauro Carvalho Chehab 	if (!buf)
1877db65c49eSMauro Carvalho Chehab 		return -ENOMEM;
1878db65c49eSMauro Carvalho Chehab 
18790aa77f6cSMauro Carvalho Chehab 	if (!bOut) {
18800aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
18810aa77f6cSMauro Carvalho Chehab 				    Request,
18820aa77f6cSMauro Carvalho Chehab 				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
18830aa77f6cSMauro Carvalho Chehab 				    USB_DIR_IN,
1884db65c49eSMauro Carvalho Chehab 				    Value, Index, buf,
1885f71d272aSJohan Hovold 				    TransferBufferLength, USB_CTRL_SET_TIMEOUT);
1886db65c49eSMauro Carvalho Chehab 
1887db65c49eSMauro Carvalho Chehab 		if (r >= 0)
1888db65c49eSMauro Carvalho Chehab 			memcpy(TransferBuffer, buf, TransferBufferLength);
18890aa77f6cSMauro Carvalho Chehab 	} else {
1890db65c49eSMauro Carvalho Chehab 		memcpy(buf, TransferBuffer, TransferBufferLength);
18910aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
18920aa77f6cSMauro Carvalho Chehab 				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1893db65c49eSMauro Carvalho Chehab 				    Value, Index, buf,
1894f71d272aSJohan Hovold 				    TransferBufferLength, USB_CTRL_SET_TIMEOUT);
18950aa77f6cSMauro Carvalho Chehab 	}
1896db65c49eSMauro Carvalho Chehab 	kfree(buf);
18970aa77f6cSMauro Carvalho Chehab 	return r;
18980aa77f6cSMauro Carvalho Chehab }
18990aa77f6cSMauro Carvalho Chehab 
19000aa77f6cSMauro Carvalho Chehab /*
19010aa77f6cSMauro Carvalho Chehab  * retrieve FX2 firmware version. future use.
19020aa77f6cSMauro Carvalho Chehab  * @param dev pointer to device extension
19030aa77f6cSMauro Carvalho Chehab  * @return -1 for fail, else returns firmware version as an int(16 bits)
19040aa77f6cSMauro Carvalho Chehab  */
s2255_get_fx2fw(struct s2255_dev * dev)19050aa77f6cSMauro Carvalho Chehab static int s2255_get_fx2fw(struct s2255_dev *dev)
19060aa77f6cSMauro Carvalho Chehab {
19070aa77f6cSMauro Carvalho Chehab 	int fw;
19080aa77f6cSMauro Carvalho Chehab 	int ret;
19090aa77f6cSMauro Carvalho Chehab 	unsigned char transBuffer[64];
19100aa77f6cSMauro Carvalho Chehab 	ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
19110aa77f6cSMauro Carvalho Chehab 			       S2255_VR_IN);
19120aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
1913f5402007Ssensoray-dev 		dprintk(dev, 2, "get fw error: %x\n", ret);
19140aa77f6cSMauro Carvalho Chehab 	fw = transBuffer[0] + (transBuffer[1] << 8);
1915f5402007Ssensoray-dev 	dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
19160aa77f6cSMauro Carvalho Chehab 	return fw;
19170aa77f6cSMauro Carvalho Chehab }
19180aa77f6cSMauro Carvalho Chehab 
19190aa77f6cSMauro Carvalho Chehab /*
19200aa77f6cSMauro Carvalho Chehab  * Create the system ring buffer to copy frames into from the
19210aa77f6cSMauro Carvalho Chehab  * usb read pipe.
19220aa77f6cSMauro Carvalho Chehab  */
s2255_create_sys_buffers(struct s2255_vc * vc)19235e950fafSDean Anderson static int s2255_create_sys_buffers(struct s2255_vc *vc)
19240aa77f6cSMauro Carvalho Chehab {
19250aa77f6cSMauro Carvalho Chehab 	unsigned long i;
19260aa77f6cSMauro Carvalho Chehab 	unsigned long reqsize;
19275e950fafSDean Anderson 	vc->buffer.dwFrames = SYS_FRAMES;
19280aa77f6cSMauro Carvalho Chehab 	/* always allocate maximum size(PAL) for system buffers */
19290aa77f6cSMauro Carvalho Chehab 	reqsize = SYS_FRAMES_MAXSIZE;
19300aa77f6cSMauro Carvalho Chehab 
19310aa77f6cSMauro Carvalho Chehab 	if (reqsize > SYS_FRAMES_MAXSIZE)
19320aa77f6cSMauro Carvalho Chehab 		reqsize = SYS_FRAMES_MAXSIZE;
19330aa77f6cSMauro Carvalho Chehab 
19340aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
19350aa77f6cSMauro Carvalho Chehab 		/* allocate the frames */
19365e950fafSDean Anderson 		vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
19375e950fafSDean Anderson 		vc->buffer.frame[i].size = reqsize;
19385e950fafSDean Anderson 		if (vc->buffer.frame[i].lpvbits == NULL) {
1939f5402007Ssensoray-dev 			pr_info("out of memory.  using less frames\n");
19405e950fafSDean Anderson 			vc->buffer.dwFrames = i;
19410aa77f6cSMauro Carvalho Chehab 			break;
19420aa77f6cSMauro Carvalho Chehab 		}
19430aa77f6cSMauro Carvalho Chehab 	}
19440aa77f6cSMauro Carvalho Chehab 
19450aa77f6cSMauro Carvalho Chehab 	/* make sure internal states are set */
19460aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
19475e950fafSDean Anderson 		vc->buffer.frame[i].ulState = 0;
19485e950fafSDean Anderson 		vc->buffer.frame[i].cur_size = 0;
19490aa77f6cSMauro Carvalho Chehab 	}
19500aa77f6cSMauro Carvalho Chehab 
19515e950fafSDean Anderson 	vc->cur_frame = 0;
19525e950fafSDean Anderson 	vc->last_frame = -1;
19530aa77f6cSMauro Carvalho Chehab 	return 0;
19540aa77f6cSMauro Carvalho Chehab }
19550aa77f6cSMauro Carvalho Chehab 
s2255_release_sys_buffers(struct s2255_vc * vc)19565e950fafSDean Anderson static int s2255_release_sys_buffers(struct s2255_vc *vc)
19570aa77f6cSMauro Carvalho Chehab {
19580aa77f6cSMauro Carvalho Chehab 	unsigned long i;
19590aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
19605e950fafSDean Anderson 		vfree(vc->buffer.frame[i].lpvbits);
19615e950fafSDean Anderson 		vc->buffer.frame[i].lpvbits = NULL;
19620aa77f6cSMauro Carvalho Chehab 	}
19630aa77f6cSMauro Carvalho Chehab 	return 0;
19640aa77f6cSMauro Carvalho Chehab }
19650aa77f6cSMauro Carvalho Chehab 
s2255_board_init(struct s2255_dev * dev)19660aa77f6cSMauro Carvalho Chehab static int s2255_board_init(struct s2255_dev *dev)
19670aa77f6cSMauro Carvalho Chehab {
19680aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
19690aa77f6cSMauro Carvalho Chehab 	int fw_ver;
19700aa77f6cSMauro Carvalho Chehab 	int j;
19710aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
1972f5402007Ssensoray-dev 	dprintk(dev, 4, "board init: %p", dev);
19730aa77f6cSMauro Carvalho Chehab 	memset(pipe, 0, sizeof(*pipe));
19740aa77f6cSMauro Carvalho Chehab 	pipe->dev = dev;
19750aa77f6cSMauro Carvalho Chehab 	pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
19760aa77f6cSMauro Carvalho Chehab 	pipe->max_transfer_size = S2255_USB_XFER_SIZE;
19770aa77f6cSMauro Carvalho Chehab 
19780aa77f6cSMauro Carvalho Chehab 	pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
19790aa77f6cSMauro Carvalho Chehab 					GFP_KERNEL);
19800aa77f6cSMauro Carvalho Chehab 	if (pipe->transfer_buffer == NULL) {
1981f5402007Ssensoray-dev 		dprintk(dev, 1, "out of memory!\n");
19820aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
19830aa77f6cSMauro Carvalho Chehab 	}
19840aa77f6cSMauro Carvalho Chehab 	/* query the firmware */
19850aa77f6cSMauro Carvalho Chehab 	fw_ver = s2255_get_fx2fw(dev);
19860aa77f6cSMauro Carvalho Chehab 
1987f5402007Ssensoray-dev 	pr_info("s2255: usb firmware version %d.%d\n",
19880aa77f6cSMauro Carvalho Chehab 		(fw_ver >> 8) & 0xff,
19890aa77f6cSMauro Carvalho Chehab 		fw_ver & 0xff);
19900aa77f6cSMauro Carvalho Chehab 
19910aa77f6cSMauro Carvalho Chehab 	if (fw_ver < S2255_CUR_USB_FWVER)
1992f5402007Ssensoray-dev 		pr_info("s2255: newer USB firmware available\n");
19930aa77f6cSMauro Carvalho Chehab 
19940aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < MAX_CHANNELS; j++) {
19955e950fafSDean Anderson 		struct s2255_vc *vc = &dev->vc[j];
19965e950fafSDean Anderson 		vc->mode = mode_def;
19970aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 && j > 1)
19985e950fafSDean Anderson 			vc->mode.color |= (1 << 16);
19995e950fafSDean Anderson 		vc->jpegqual = S2255_DEF_JPEG_QUAL;
20005e950fafSDean Anderson 		vc->width = LINE_SZ_4CIFS_NTSC;
20015e950fafSDean Anderson 		vc->height = NUM_LINES_4CIFS_NTSC * 2;
20025e950fafSDean Anderson 		vc->std = V4L2_STD_NTSC_M;
20035e950fafSDean Anderson 		vc->fmt = &formats[0];
20045e950fafSDean Anderson 		vc->mode.restart = 1;
20055e950fafSDean Anderson 		vc->req_image_size = get_transfer_size(&mode_def);
20065e950fafSDean Anderson 		vc->frame_count = 0;
20070aa77f6cSMauro Carvalho Chehab 		/* create the system buffers */
20085e950fafSDean Anderson 		s2255_create_sys_buffers(vc);
20090aa77f6cSMauro Carvalho Chehab 	}
20100aa77f6cSMauro Carvalho Chehab 	/* start read pipe */
20110aa77f6cSMauro Carvalho Chehab 	s2255_start_readpipe(dev);
2012f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: success\n", __func__);
20130aa77f6cSMauro Carvalho Chehab 	return 0;
20140aa77f6cSMauro Carvalho Chehab }
20150aa77f6cSMauro Carvalho Chehab 
s2255_board_shutdown(struct s2255_dev * dev)20160aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev)
20170aa77f6cSMauro Carvalho Chehab {
20180aa77f6cSMauro Carvalho Chehab 	u32 i;
2019f5402007Ssensoray-dev 	dprintk(dev, 1, "%s: dev: %p", __func__,  dev);
20200aa77f6cSMauro Carvalho Chehab 
20210aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
2022340a30c5Ssensoray-dev 		if (vb2_is_streaming(&dev->vc[i].vb_vidq))
20235e950fafSDean Anderson 			s2255_stop_acquire(&dev->vc[i]);
20240aa77f6cSMauro Carvalho Chehab 	}
20250aa77f6cSMauro Carvalho Chehab 	s2255_stop_readpipe(dev);
20260aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++)
20275e950fafSDean Anderson 		s2255_release_sys_buffers(&dev->vc[i]);
20280aa77f6cSMauro Carvalho Chehab 	/* release transfer buffer */
20290aa77f6cSMauro Carvalho Chehab 	kfree(dev->pipe.transfer_buffer);
20300aa77f6cSMauro Carvalho Chehab 	return 0;
20310aa77f6cSMauro Carvalho Chehab }
20320aa77f6cSMauro Carvalho Chehab 
read_pipe_completion(struct urb * purb)20330aa77f6cSMauro Carvalho Chehab static void read_pipe_completion(struct urb *purb)
20340aa77f6cSMauro Carvalho Chehab {
20350aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info;
20360aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev;
20370aa77f6cSMauro Carvalho Chehab 	int status;
20380aa77f6cSMauro Carvalho Chehab 	int pipe;
20390aa77f6cSMauro Carvalho Chehab 	pipe_info = purb->context;
20400aa77f6cSMauro Carvalho Chehab 	if (pipe_info == NULL) {
20410aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
20420aa77f6cSMauro Carvalho Chehab 		return;
20430aa77f6cSMauro Carvalho Chehab 	}
20440aa77f6cSMauro Carvalho Chehab 	dev = pipe_info->dev;
20450aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
20460aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
20470aa77f6cSMauro Carvalho Chehab 		return;
20480aa77f6cSMauro Carvalho Chehab 	}
20490aa77f6cSMauro Carvalho Chehab 	status = purb->status;
20500aa77f6cSMauro Carvalho Chehab 	/* if shutting down, do not resubmit, exit immediately */
20510aa77f6cSMauro Carvalho Chehab 	if (status == -ESHUTDOWN) {
2052f5402007Ssensoray-dev 		dprintk(dev, 2, "%s: err shutdown\n", __func__);
20530aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
20540aa77f6cSMauro Carvalho Chehab 		return;
20550aa77f6cSMauro Carvalho Chehab 	}
20560aa77f6cSMauro Carvalho Chehab 
20570aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state == 0) {
2058f5402007Ssensoray-dev 		dprintk(dev, 2, "%s: exiting USB pipe", __func__);
20590aa77f6cSMauro Carvalho Chehab 		return;
20600aa77f6cSMauro Carvalho Chehab 	}
20610aa77f6cSMauro Carvalho Chehab 
20620aa77f6cSMauro Carvalho Chehab 	if (status == 0)
20630aa77f6cSMauro Carvalho Chehab 		s2255_read_video_callback(dev, pipe_info);
20640aa77f6cSMauro Carvalho Chehab 	else {
20650aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
2066f5402007Ssensoray-dev 		dprintk(dev, 1, "%s: failed URB %d\n", __func__, status);
20670aa77f6cSMauro Carvalho Chehab 	}
20680aa77f6cSMauro Carvalho Chehab 
20690aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
20700aa77f6cSMauro Carvalho Chehab 	/* reuse urb */
20710aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
20720aa77f6cSMauro Carvalho Chehab 			  pipe,
20730aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
20740aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
20750aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
20760aa77f6cSMauro Carvalho Chehab 
20770aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state != 0) {
2078f5402007Ssensoray-dev 		if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC))
20790aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev, "error submitting urb\n");
20800aa77f6cSMauro Carvalho Chehab 	} else {
2081f5402007Ssensoray-dev 		dprintk(dev, 2, "%s :complete state 0\n", __func__);
20820aa77f6cSMauro Carvalho Chehab 	}
20830aa77f6cSMauro Carvalho Chehab 	return;
20840aa77f6cSMauro Carvalho Chehab }
20850aa77f6cSMauro Carvalho Chehab 
s2255_start_readpipe(struct s2255_dev * dev)20860aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev)
20870aa77f6cSMauro Carvalho Chehab {
20880aa77f6cSMauro Carvalho Chehab 	int pipe;
20890aa77f6cSMauro Carvalho Chehab 	int retval;
20900aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info = &dev->pipe;
20910aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
2092f5402007Ssensoray-dev 	dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint);
20930aa77f6cSMauro Carvalho Chehab 	pipe_info->state = 1;
20940aa77f6cSMauro Carvalho Chehab 	pipe_info->err_count = 0;
20950aa77f6cSMauro Carvalho Chehab 	pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
2096fc56da79SWolfram Sang 	if (!pipe_info->stream_urb)
20970aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
20980aa77f6cSMauro Carvalho Chehab 	/* transfer buffer allocated in board_init */
20990aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
21000aa77f6cSMauro Carvalho Chehab 			  pipe,
21010aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
21020aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
21030aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
21040aa77f6cSMauro Carvalho Chehab 	retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
21050aa77f6cSMauro Carvalho Chehab 	if (retval) {
2106f5402007Ssensoray-dev 		pr_err("s2255: start read pipe failed\n");
21070aa77f6cSMauro Carvalho Chehab 		return retval;
21080aa77f6cSMauro Carvalho Chehab 	}
21090aa77f6cSMauro Carvalho Chehab 	return 0;
21100aa77f6cSMauro Carvalho Chehab }
21110aa77f6cSMauro Carvalho Chehab 
21120aa77f6cSMauro Carvalho Chehab /* starts acquisition process */
s2255_start_acquire(struct s2255_vc * vc)21135e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc)
21140aa77f6cSMauro Carvalho Chehab {
21150aa77f6cSMauro Carvalho Chehab 	int res;
21160aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
21170aa77f6cSMauro Carvalho Chehab 	int j;
21185e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
211947d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
21200aa77f6cSMauro Carvalho Chehab 
212147d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
212247d8c881SDean Anderson 	chn_rev = G_chnmap[vc->idx];
21235e950fafSDean Anderson 	vc->last_frame = -1;
21245e950fafSDean Anderson 	vc->bad_payload = 0;
21255e950fafSDean Anderson 	vc->cur_frame = 0;
21260aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
21275e950fafSDean Anderson 		vc->buffer.frame[j].ulState = 0;
21285e950fafSDean Anderson 		vc->buffer.frame[j].cur_size = 0;
21290aa77f6cSMauro Carvalho Chehab 	}
21300aa77f6cSMauro Carvalho Chehab 
21310aa77f6cSMauro Carvalho Chehab 	/* send the start command */
213247d8c881SDean Anderson 	buffer[0] = IN_DATA_TOKEN;
213347d8c881SDean Anderson 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
213447d8c881SDean Anderson 	buffer[2] = CMD_START;
21350aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
21360aa77f6cSMauro Carvalho Chehab 	if (res != 0)
21370aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_START error\n");
21380aa77f6cSMauro Carvalho Chehab 
21395e950fafSDean Anderson 	dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res);
214047d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
21416a5b63b3SDean Anderson 	return res;
21420aa77f6cSMauro Carvalho Chehab }
21430aa77f6cSMauro Carvalho Chehab 
s2255_stop_acquire(struct s2255_vc * vc)21445e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc)
21450aa77f6cSMauro Carvalho Chehab {
21460aa77f6cSMauro Carvalho Chehab 	int res;
21470aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
21485e950fafSDean Anderson 	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
214947d8c881SDean Anderson 	__le32 *buffer = dev->cmdbuf;
215047d8c881SDean Anderson 
215147d8c881SDean Anderson 	mutex_lock(&dev->cmdlock);
21525e950fafSDean Anderson 	chn_rev = G_chnmap[vc->idx];
21530aa77f6cSMauro Carvalho Chehab 	/* send the stop command */
215447d8c881SDean Anderson 	buffer[0] = IN_DATA_TOKEN;
215547d8c881SDean Anderson 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
215647d8c881SDean Anderson 	buffer[2] = CMD_STOP;
215747d8c881SDean Anderson 
21580aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
21590aa77f6cSMauro Carvalho Chehab 	if (res != 0)
21600aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_STOP error\n");
216147d8c881SDean Anderson 
21625e950fafSDean Anderson 	dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res);
216347d8c881SDean Anderson 	mutex_unlock(&dev->cmdlock);
21640aa77f6cSMauro Carvalho Chehab 	return res;
21650aa77f6cSMauro Carvalho Chehab }
21660aa77f6cSMauro Carvalho Chehab 
s2255_stop_readpipe(struct s2255_dev * dev)21670aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev)
21680aa77f6cSMauro Carvalho Chehab {
21690aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
21700aa77f6cSMauro Carvalho Chehab 
21710aa77f6cSMauro Carvalho Chehab 	pipe->state = 0;
21720aa77f6cSMauro Carvalho Chehab 	if (pipe->stream_urb) {
21730aa77f6cSMauro Carvalho Chehab 		/* cancel urb */
21740aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(pipe->stream_urb);
21750aa77f6cSMauro Carvalho Chehab 		usb_free_urb(pipe->stream_urb);
21760aa77f6cSMauro Carvalho Chehab 		pipe->stream_urb = NULL;
21770aa77f6cSMauro Carvalho Chehab 	}
2178f5402007Ssensoray-dev 	dprintk(dev, 4, "%s", __func__);
21790aa77f6cSMauro Carvalho Chehab 	return;
21800aa77f6cSMauro Carvalho Chehab }
21810aa77f6cSMauro Carvalho Chehab 
s2255_fwload_start(struct s2255_dev * dev)2182e2a06704SDean A static void s2255_fwload_start(struct s2255_dev *dev)
21830aa77f6cSMauro Carvalho Chehab {
21840aa77f6cSMauro Carvalho Chehab 	s2255_reset_dsppower(dev);
21850aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_size = dev->fw_data->fw->size;
21860aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
21870aa77f6cSMauro Carvalho Chehab 	memcpy(dev->fw_data->pfw_data,
21880aa77f6cSMauro Carvalho Chehab 	       dev->fw_data->fw->data, CHUNK_SIZE);
21890aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_loaded = CHUNK_SIZE;
21900aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
21910aa77f6cSMauro Carvalho Chehab 			  usb_sndbulkpipe(dev->udev, 2),
21920aa77f6cSMauro Carvalho Chehab 			  dev->fw_data->pfw_data,
21930aa77f6cSMauro Carvalho Chehab 			  CHUNK_SIZE, s2255_fwchunk_complete,
21940aa77f6cSMauro Carvalho Chehab 			  dev->fw_data);
21950aa77f6cSMauro Carvalho Chehab 	mod_timer(&dev->timer, jiffies + HZ);
21960aa77f6cSMauro Carvalho Chehab }
21970aa77f6cSMauro Carvalho Chehab 
21980aa77f6cSMauro Carvalho Chehab /* standard usb probe function */
s2255_probe(struct usb_interface * interface,const struct usb_device_id * id)21990aa77f6cSMauro Carvalho Chehab static int s2255_probe(struct usb_interface *interface,
22000aa77f6cSMauro Carvalho Chehab 		       const struct usb_device_id *id)
22010aa77f6cSMauro Carvalho Chehab {
22020aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = NULL;
22030aa77f6cSMauro Carvalho Chehab 	struct usb_host_interface *iface_desc;
22040aa77f6cSMauro Carvalho Chehab 	struct usb_endpoint_descriptor *endpoint;
22050aa77f6cSMauro Carvalho Chehab 	int i;
22060aa77f6cSMauro Carvalho Chehab 	int retval = -ENOMEM;
22070aa77f6cSMauro Carvalho Chehab 	__le32 *pdata;
22080aa77f6cSMauro Carvalho Chehab 	int fw_size;
220947d8c881SDean Anderson 
22100aa77f6cSMauro Carvalho Chehab 	/* allocate memory for our device state and initialize it to zero */
22110aa77f6cSMauro Carvalho Chehab 	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
22120aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
22130aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&interface->dev, "out of memory\n");
22140aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
22150aa77f6cSMauro Carvalho Chehab 	}
221647d8c881SDean Anderson 
221747d8c881SDean Anderson 	dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
221847d8c881SDean Anderson 	if (dev->cmdbuf == NULL) {
221947d8c881SDean Anderson 		s2255_dev_err(&interface->dev, "out of memory\n");
2220e21c94e7SDaeseok Youn 		goto errorFWDATA1;
222147d8c881SDean Anderson 	}
222247d8c881SDean Anderson 
2223*29ce81f9SRicardo Ribalda 	refcount_set(&dev->num_channels, 0);
2224ff3ec57dSHans Verkuil 	dev->pid = id->idProduct;
22250aa77f6cSMauro Carvalho Chehab 	dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
22260aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data)
22270aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA1;
22280aa77f6cSMauro Carvalho Chehab 	mutex_init(&dev->lock);
222947d8c881SDean Anderson 	mutex_init(&dev->cmdlock);
22300aa77f6cSMauro Carvalho Chehab 	/* grab usb_device and save it */
22310aa77f6cSMauro Carvalho Chehab 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
22320aa77f6cSMauro Carvalho Chehab 	if (dev->udev == NULL) {
22330aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "null usb device\n");
22340aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
22350aa77f6cSMauro Carvalho Chehab 		goto errorUDEV;
22360aa77f6cSMauro Carvalho Chehab 	}
2237f5402007Ssensoray-dev 	dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
2238f5402007Ssensoray-dev 		dev, dev->udev, interface);
22390aa77f6cSMauro Carvalho Chehab 	dev->interface = interface;
22400aa77f6cSMauro Carvalho Chehab 	/* set up the endpoint information  */
22410aa77f6cSMauro Carvalho Chehab 	iface_desc = interface->cur_altsetting;
2242f5402007Ssensoray-dev 	dev_dbg(&interface->dev, "num EP: %d\n",
2243f5402007Ssensoray-dev 		iface_desc->desc.bNumEndpoints);
22440aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
22450aa77f6cSMauro Carvalho Chehab 		endpoint = &iface_desc->endpoint[i].desc;
22460aa77f6cSMauro Carvalho Chehab 		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
22470aa77f6cSMauro Carvalho Chehab 			/* we found the bulk in endpoint */
22480aa77f6cSMauro Carvalho Chehab 			dev->read_endpoint = endpoint->bEndpointAddress;
22490aa77f6cSMauro Carvalho Chehab 		}
22500aa77f6cSMauro Carvalho Chehab 	}
22510aa77f6cSMauro Carvalho Chehab 
22520aa77f6cSMauro Carvalho Chehab 	if (!dev->read_endpoint) {
22530aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
22540aa77f6cSMauro Carvalho Chehab 		goto errorEP;
22550aa77f6cSMauro Carvalho Chehab 	}
225674ee0477SKees Cook 	timer_setup(&dev->timer, s2255_timer, 0);
22570aa77f6cSMauro Carvalho Chehab 	init_waitqueue_head(&dev->fw_data->wait_fw);
22580aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
22595e950fafSDean Anderson 		struct s2255_vc *vc = &dev->vc[i];
22605e950fafSDean Anderson 		vc->idx = i;
22615e950fafSDean Anderson 		vc->dev = dev;
22625e950fafSDean Anderson 		init_waitqueue_head(&vc->wait_setmode);
22635e950fafSDean Anderson 		init_waitqueue_head(&vc->wait_vidstatus);
2264340a30c5Ssensoray-dev 		spin_lock_init(&vc->qlock);
2265340a30c5Ssensoray-dev 		mutex_init(&vc->vb_lock);
22660aa77f6cSMauro Carvalho Chehab 	}
22670aa77f6cSMauro Carvalho Chehab 
22680aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
2269fc56da79SWolfram Sang 	if (!dev->fw_data->fw_urb)
22700aa77f6cSMauro Carvalho Chehab 		goto errorFWURB;
22710aa77f6cSMauro Carvalho Chehab 
22720aa77f6cSMauro Carvalho Chehab 	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
22730aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data->pfw_data) {
22740aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "out of memory!\n");
22750aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA2;
22760aa77f6cSMauro Carvalho Chehab 	}
22770aa77f6cSMauro Carvalho Chehab 	/* load the first chunk */
22780aa77f6cSMauro Carvalho Chehab 	if (request_firmware(&dev->fw_data->fw,
22790aa77f6cSMauro Carvalho Chehab 			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
2280f5402007Ssensoray-dev 		dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n");
22810aa77f6cSMauro Carvalho Chehab 		goto errorREQFW;
22820aa77f6cSMauro Carvalho Chehab 	}
22830aa77f6cSMauro Carvalho Chehab 	/* check the firmware is valid */
22840aa77f6cSMauro Carvalho Chehab 	fw_size = dev->fw_data->fw->size;
22850aa77f6cSMauro Carvalho Chehab 	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
22860aa77f6cSMauro Carvalho Chehab 
22870aa77f6cSMauro Carvalho Chehab 	if (*pdata != S2255_FW_MARKER) {
2288f5402007Ssensoray-dev 		dev_err(&interface->dev, "Firmware invalid.\n");
22890aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
22900aa77f6cSMauro Carvalho Chehab 		goto errorFWMARKER;
22910aa77f6cSMauro Carvalho Chehab 	} else {
22920aa77f6cSMauro Carvalho Chehab 		/* make sure firmware is the latest */
22930aa77f6cSMauro Carvalho Chehab 		__le32 *pRel;
22940aa77f6cSMauro Carvalho Chehab 		pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
2295f5402007Ssensoray-dev 		pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
22960aa77f6cSMauro Carvalho Chehab 		dev->dsp_fw_ver = le32_to_cpu(*pRel);
22970aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
2298f5402007Ssensoray-dev 			pr_info("s2255: f2255usb.bin out of date.\n");
22990aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 &&
23000aa77f6cSMauro Carvalho Chehab 				dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
2301f5402007Ssensoray-dev 			pr_warn("2257 needs firmware %d or above.\n",
2302f5402007Ssensoray-dev 				S2255_MIN_DSP_COLORFILTER);
23030aa77f6cSMauro Carvalho Chehab 	}
23040aa77f6cSMauro Carvalho Chehab 	usb_reset_device(dev->udev);
23050aa77f6cSMauro Carvalho Chehab 	/* load 2255 board specific */
23060aa77f6cSMauro Carvalho Chehab 	retval = s2255_board_init(dev);
23070aa77f6cSMauro Carvalho Chehab 	if (retval)
23080aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
2309e2a06704SDean A 	s2255_fwload_start(dev);
23100aa77f6cSMauro Carvalho Chehab 	/* loads v4l specific */
23110aa77f6cSMauro Carvalho Chehab 	retval = s2255_probe_v4l(dev);
23120aa77f6cSMauro Carvalho Chehab 	if (retval)
23130aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
23140aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "Sensoray 2255 detected\n");
23150aa77f6cSMauro Carvalho Chehab 	return 0;
23160aa77f6cSMauro Carvalho Chehab errorBOARDINIT:
23170aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
23180aa77f6cSMauro Carvalho Chehab errorFWMARKER:
23190aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
23200aa77f6cSMauro Carvalho Chehab errorREQFW:
23210aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
23220aa77f6cSMauro Carvalho Chehab errorFWDATA2:
23230aa77f6cSMauro Carvalho Chehab 	usb_free_urb(dev->fw_data->fw_urb);
23240aa77f6cSMauro Carvalho Chehab errorFWURB:
2325292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&dev->timer);
23260aa77f6cSMauro Carvalho Chehab errorEP:
23270aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
23280aa77f6cSMauro Carvalho Chehab errorUDEV:
23290aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
23300aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
23310aa77f6cSMauro Carvalho Chehab errorFWDATA1:
233247d8c881SDean Anderson 	kfree(dev->cmdbuf);
23330aa77f6cSMauro Carvalho Chehab 	kfree(dev);
2334f5402007Ssensoray-dev 	pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval);
23350aa77f6cSMauro Carvalho Chehab 	return retval;
23360aa77f6cSMauro Carvalho Chehab }
23370aa77f6cSMauro Carvalho Chehab 
23380aa77f6cSMauro Carvalho Chehab /* disconnect routine. when board is removed physically or with rmmod */
s2255_disconnect(struct usb_interface * interface)23390aa77f6cSMauro Carvalho Chehab static void s2255_disconnect(struct usb_interface *interface)
23400aa77f6cSMauro Carvalho Chehab {
23410aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
23420aa77f6cSMauro Carvalho Chehab 	int i;
2343*29ce81f9SRicardo Ribalda 	int channels = refcount_read(&dev->num_channels);
23440aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
23450aa77f6cSMauro Carvalho Chehab 	v4l2_device_disconnect(&dev->v4l2_dev);
23460aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
23470aa77f6cSMauro Carvalho Chehab 	/*see comments in the uvc_driver.c usb disconnect function */
2348*29ce81f9SRicardo Ribalda 	refcount_inc(&dev->num_channels);
23490aa77f6cSMauro Carvalho Chehab 	/* unregister each video device. */
23500aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < channels; i++)
23515e950fafSDean Anderson 		video_unregister_device(&dev->vc[i].vdev);
23520aa77f6cSMauro Carvalho Chehab 	/* wake up any of our timers */
23530aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
23540aa77f6cSMauro Carvalho Chehab 	wake_up(&dev->fw_data->wait_fw);
23550aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
23565e950fafSDean Anderson 		dev->vc[i].setmode_ready = 1;
23575e950fafSDean Anderson 		wake_up(&dev->vc[i].wait_setmode);
23585e950fafSDean Anderson 		dev->vc[i].vidstatus_ready = 1;
23595e950fafSDean Anderson 		wake_up(&dev->vc[i].wait_vidstatus);
23600aa77f6cSMauro Carvalho Chehab 	}
2361*29ce81f9SRicardo Ribalda 	if (refcount_dec_and_test(&dev->num_channels))
23620aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
23630aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "%s\n", __func__);
23640aa77f6cSMauro Carvalho Chehab }
23650aa77f6cSMauro Carvalho Chehab 
23660aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver = {
23670aa77f6cSMauro Carvalho Chehab 	.name = S2255_DRIVER_NAME,
23680aa77f6cSMauro Carvalho Chehab 	.probe = s2255_probe,
23690aa77f6cSMauro Carvalho Chehab 	.disconnect = s2255_disconnect,
23700aa77f6cSMauro Carvalho Chehab 	.id_table = s2255_table,
23710aa77f6cSMauro Carvalho Chehab };
23720aa77f6cSMauro Carvalho Chehab 
23730aa77f6cSMauro Carvalho Chehab module_usb_driver(s2255_driver);
23740aa77f6cSMauro Carvalho Chehab 
23750aa77f6cSMauro Carvalho Chehab MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
23760aa77f6cSMauro Carvalho Chehab MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
23770aa77f6cSMauro Carvalho Chehab MODULE_LICENSE("GPL");
23780aa77f6cSMauro Carvalho Chehab MODULE_VERSION(S2255_VERSION);
23790aa77f6cSMauro Carvalho Chehab MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
2380