xref: /openbmc/linux/drivers/media/usb/s2255/s2255drv.c (revision d88aab53bd267c9fb0b0451e9acae68091703eab)
10aa77f6cSMauro Carvalho Chehab /*
20aa77f6cSMauro Carvalho Chehab  *  s2255drv.c - a driver for the Sensoray 2255 USB video capture device
30aa77f6cSMauro Carvalho Chehab  *
40aa77f6cSMauro Carvalho Chehab  *   Copyright (C) 2007-2010 by Sensoray Company Inc.
50aa77f6cSMauro Carvalho Chehab  *                              Dean Anderson
60aa77f6cSMauro Carvalho Chehab  *
70aa77f6cSMauro Carvalho Chehab  * Some video buffer code based on vivi driver:
80aa77f6cSMauro Carvalho Chehab  *
90aa77f6cSMauro Carvalho Chehab  * Sensoray 2255 device supports 4 simultaneous channels.
100aa77f6cSMauro Carvalho Chehab  * The channels are not "crossbar" inputs, they are physically
110aa77f6cSMauro Carvalho Chehab  * attached to separate video decoders.
120aa77f6cSMauro Carvalho Chehab  *
130aa77f6cSMauro Carvalho Chehab  * Because of USB2.0 bandwidth limitations. There is only a
140aa77f6cSMauro Carvalho Chehab  * certain amount of data which may be transferred at one time.
150aa77f6cSMauro Carvalho Chehab  *
160aa77f6cSMauro Carvalho Chehab  * Example maximum bandwidth utilization:
170aa77f6cSMauro Carvalho Chehab  *
180aa77f6cSMauro Carvalho Chehab  * -full size, color mode YUYV or YUV422P: 2 channels at once
190aa77f6cSMauro Carvalho Chehab  * -full or half size Grey scale: all 4 channels at once
200aa77f6cSMauro Carvalho Chehab  * -half size, color mode YUYV or YUV422P: all 4 channels at once
210aa77f6cSMauro Carvalho Chehab  * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
220aa77f6cSMauro Carvalho Chehab  *  at once.
230aa77f6cSMauro Carvalho Chehab  *
240aa77f6cSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
250aa77f6cSMauro Carvalho Chehab  * it under the terms of the GNU General Public License as published by
260aa77f6cSMauro Carvalho Chehab  * the Free Software Foundation; either version 2 of the License, or
270aa77f6cSMauro Carvalho Chehab  * (at your option) any later version.
280aa77f6cSMauro Carvalho Chehab  *
290aa77f6cSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
300aa77f6cSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
310aa77f6cSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
320aa77f6cSMauro Carvalho Chehab  * GNU General Public License for more details.
330aa77f6cSMauro Carvalho Chehab  *
340aa77f6cSMauro Carvalho Chehab  * You should have received a copy of the GNU General Public License
350aa77f6cSMauro Carvalho Chehab  * along with this program; if not, write to the Free Software
360aa77f6cSMauro Carvalho Chehab  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
370aa77f6cSMauro Carvalho Chehab  */
380aa77f6cSMauro Carvalho Chehab 
390aa77f6cSMauro Carvalho Chehab #include <linux/module.h>
400aa77f6cSMauro Carvalho Chehab #include <linux/firmware.h>
410aa77f6cSMauro Carvalho Chehab #include <linux/kernel.h>
420aa77f6cSMauro Carvalho Chehab #include <linux/mutex.h>
430aa77f6cSMauro Carvalho Chehab #include <linux/slab.h>
440aa77f6cSMauro Carvalho Chehab #include <linux/videodev2.h>
450aa77f6cSMauro Carvalho Chehab #include <linux/mm.h>
460aa77f6cSMauro Carvalho Chehab #include <media/videobuf-vmalloc.h>
470aa77f6cSMauro Carvalho Chehab #include <media/v4l2-common.h>
480aa77f6cSMauro Carvalho Chehab #include <media/v4l2-device.h>
490aa77f6cSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
500aa77f6cSMauro Carvalho Chehab #include <linux/vmalloc.h>
510aa77f6cSMauro Carvalho Chehab #include <linux/usb.h>
520aa77f6cSMauro Carvalho Chehab 
530aa77f6cSMauro Carvalho Chehab #define S2255_VERSION		"1.22.1"
540aa77f6cSMauro Carvalho Chehab #define FIRMWARE_FILE_NAME "f2255usb.bin"
550aa77f6cSMauro Carvalho Chehab 
560aa77f6cSMauro Carvalho Chehab /* default JPEG quality */
570aa77f6cSMauro Carvalho Chehab #define S2255_DEF_JPEG_QUAL     50
580aa77f6cSMauro Carvalho Chehab /* vendor request in */
590aa77f6cSMauro Carvalho Chehab #define S2255_VR_IN		0
600aa77f6cSMauro Carvalho Chehab /* vendor request out */
610aa77f6cSMauro Carvalho Chehab #define S2255_VR_OUT		1
620aa77f6cSMauro Carvalho Chehab /* firmware query */
630aa77f6cSMauro Carvalho Chehab #define S2255_VR_FW		0x30
640aa77f6cSMauro Carvalho Chehab /* USB endpoint number for configuring the device */
650aa77f6cSMauro Carvalho Chehab #define S2255_CONFIG_EP         2
660aa77f6cSMauro Carvalho Chehab /* maximum time for DSP to start responding after last FW word loaded(ms) */
670aa77f6cSMauro Carvalho Chehab #define S2255_DSP_BOOTTIME      800
680aa77f6cSMauro Carvalho Chehab /* maximum time to wait for firmware to load (ms) */
690aa77f6cSMauro Carvalho Chehab #define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
700aa77f6cSMauro Carvalho Chehab #define S2255_DEF_BUFS          16
710aa77f6cSMauro Carvalho Chehab #define S2255_SETMODE_TIMEOUT   500
720aa77f6cSMauro Carvalho Chehab #define S2255_VIDSTATUS_TIMEOUT 350
730aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_FRAME	cpu_to_le32(0x2255DA4AL)
740aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_RESPONSE	cpu_to_le32(0x2255ACACL)
750aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_SETMODE  cpu_to_le32(0x01)
760aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_FW       cpu_to_le32(0x10)
770aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_STATUS   cpu_to_le32(0x20)
780aa77f6cSMauro Carvalho Chehab #define S2255_USB_XFER_SIZE	(16 * 1024)
790aa77f6cSMauro Carvalho Chehab #define MAX_CHANNELS		4
800aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES		4
810aa77f6cSMauro Carvalho Chehab /* maximum size is PAL full size plus room for the marker header(s) */
820aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
830aa77f6cSMauro Carvalho Chehab #define DEF_USB_BLOCK		S2255_USB_XFER_SIZE
840aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_NTSC	640
850aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_NTSC	640
860aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_NTSC	320
870aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_PAL	704
880aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_PAL	704
890aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_PAL	352
900aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_NTSC	240
910aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_NTSC	240
920aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_NTSC	240
930aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_PAL	288
940aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_PAL	288
950aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_PAL	288
960aa77f6cSMauro Carvalho Chehab #define LINE_SZ_DEF		640
970aa77f6cSMauro Carvalho Chehab #define NUM_LINES_DEF		240
980aa77f6cSMauro Carvalho Chehab 
990aa77f6cSMauro Carvalho Chehab 
1000aa77f6cSMauro Carvalho Chehab /* predefined settings */
1010aa77f6cSMauro Carvalho Chehab #define FORMAT_NTSC	1
1020aa77f6cSMauro Carvalho Chehab #define FORMAT_PAL	2
1030aa77f6cSMauro Carvalho Chehab 
1040aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
1050aa77f6cSMauro Carvalho Chehab #define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
1060aa77f6cSMauro Carvalho Chehab #define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
1070aa77f6cSMauro Carvalho Chehab /* SCALE_4CIFSI is the 2 fields interpolated into one */
1080aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFSI	4	/* 640x480(NTSC) or 704x576(PAL) high quality */
1090aa77f6cSMauro Carvalho Chehab 
1100aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPL	1	/* YUV planar */
1110aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPK	2	/* YUV packed */
1120aa77f6cSMauro Carvalho Chehab #define COLOR_Y8	4	/* monochrome */
1130aa77f6cSMauro Carvalho Chehab #define COLOR_JPG       5       /* JPEG */
1140aa77f6cSMauro Carvalho Chehab 
1150aa77f6cSMauro Carvalho Chehab #define MASK_COLOR       0x000000ff
1160aa77f6cSMauro Carvalho Chehab #define MASK_JPG_QUALITY 0x0000ff00
1170aa77f6cSMauro Carvalho Chehab #define MASK_INPUT_TYPE  0x000f0000
1180aa77f6cSMauro Carvalho Chehab /* frame decimation. */
1190aa77f6cSMauro Carvalho Chehab #define FDEC_1		1	/* capture every frame. default */
1200aa77f6cSMauro Carvalho Chehab #define FDEC_2		2	/* capture every 2nd frame */
1210aa77f6cSMauro Carvalho Chehab #define FDEC_3		3	/* capture every 3rd frame */
1220aa77f6cSMauro Carvalho Chehab #define FDEC_5		5	/* capture every 5th frame */
1230aa77f6cSMauro Carvalho Chehab 
1240aa77f6cSMauro Carvalho Chehab /*-------------------------------------------------------
1250aa77f6cSMauro Carvalho Chehab  * Default mode parameters.
1260aa77f6cSMauro Carvalho Chehab  *-------------------------------------------------------*/
1270aa77f6cSMauro Carvalho Chehab #define DEF_SCALE	SCALE_4CIFS
1280aa77f6cSMauro Carvalho Chehab #define DEF_COLOR	COLOR_YUVPL
1290aa77f6cSMauro Carvalho Chehab #define DEF_FDEC	FDEC_1
1300aa77f6cSMauro Carvalho Chehab #define DEF_BRIGHT	0
1310aa77f6cSMauro Carvalho Chehab #define DEF_CONTRAST	0x5c
1320aa77f6cSMauro Carvalho Chehab #define DEF_SATURATION	0x80
1330aa77f6cSMauro Carvalho Chehab #define DEF_HUE		0
1340aa77f6cSMauro Carvalho Chehab 
1350aa77f6cSMauro Carvalho Chehab /* usb config commands */
1360aa77f6cSMauro Carvalho Chehab #define IN_DATA_TOKEN	cpu_to_le32(0x2255c0de)
1370aa77f6cSMauro Carvalho Chehab #define CMD_2255	0xc2255000
1380aa77f6cSMauro Carvalho Chehab #define CMD_SET_MODE	cpu_to_le32((CMD_2255 | 0x10))
1390aa77f6cSMauro Carvalho Chehab #define CMD_START	cpu_to_le32((CMD_2255 | 0x20))
1400aa77f6cSMauro Carvalho Chehab #define CMD_STOP	cpu_to_le32((CMD_2255 | 0x30))
1410aa77f6cSMauro Carvalho Chehab #define CMD_STATUS	cpu_to_le32((CMD_2255 | 0x40))
1420aa77f6cSMauro Carvalho Chehab 
1430aa77f6cSMauro Carvalho Chehab struct s2255_mode {
1440aa77f6cSMauro Carvalho Chehab 	u32 format;	/* input video format (NTSC, PAL) */
1450aa77f6cSMauro Carvalho Chehab 	u32 scale;	/* output video scale */
1460aa77f6cSMauro Carvalho Chehab 	u32 color;	/* output video color format */
1470aa77f6cSMauro Carvalho Chehab 	u32 fdec;	/* frame decimation */
1480aa77f6cSMauro Carvalho Chehab 	u32 bright;	/* brightness */
1490aa77f6cSMauro Carvalho Chehab 	u32 contrast;	/* contrast */
1500aa77f6cSMauro Carvalho Chehab 	u32 saturation;	/* saturation */
1510aa77f6cSMauro Carvalho Chehab 	u32 hue;	/* hue (NTSC only)*/
1520aa77f6cSMauro Carvalho Chehab 	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
1530aa77f6cSMauro Carvalho Chehab 	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
1540aa77f6cSMauro Carvalho Chehab 	u32 restart;	/* if DSP requires restart */
1550aa77f6cSMauro Carvalho Chehab };
1560aa77f6cSMauro Carvalho Chehab 
1570aa77f6cSMauro Carvalho Chehab 
1580aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE		0
1590aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME	1
1600aa77f6cSMauro Carvalho Chehab 
1610aa77f6cSMauro Carvalho Chehab /* frame structure */
1620aa77f6cSMauro Carvalho Chehab struct s2255_framei {
1630aa77f6cSMauro Carvalho Chehab 	unsigned long size;
1640aa77f6cSMauro Carvalho Chehab 	unsigned long ulState;	/* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
1650aa77f6cSMauro Carvalho Chehab 	void *lpvbits;		/* image data */
1660aa77f6cSMauro Carvalho Chehab 	unsigned long cur_size;	/* current data copied to it */
1670aa77f6cSMauro Carvalho Chehab };
1680aa77f6cSMauro Carvalho Chehab 
1690aa77f6cSMauro Carvalho Chehab /* image buffer structure */
1700aa77f6cSMauro Carvalho Chehab struct s2255_bufferi {
1710aa77f6cSMauro Carvalho Chehab 	unsigned long dwFrames;			/* number of frames in buffer */
1720aa77f6cSMauro Carvalho Chehab 	struct s2255_framei frame[SYS_FRAMES];	/* array of FRAME structures */
1730aa77f6cSMauro Carvalho Chehab };
1740aa77f6cSMauro Carvalho Chehab 
1750aa77f6cSMauro Carvalho Chehab #define DEF_MODEI_NTSC_CONT	{FORMAT_NTSC, DEF_SCALE, DEF_COLOR,	\
1760aa77f6cSMauro Carvalho Chehab 			DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
1770aa77f6cSMauro Carvalho Chehab 			DEF_HUE, 0, DEF_USB_BLOCK, 0}
1780aa77f6cSMauro Carvalho Chehab 
1790aa77f6cSMauro Carvalho Chehab struct s2255_dmaqueue {
1800aa77f6cSMauro Carvalho Chehab 	struct list_head	active;
1810aa77f6cSMauro Carvalho Chehab 	struct s2255_dev	*dev;
1820aa77f6cSMauro Carvalho Chehab };
1830aa77f6cSMauro Carvalho Chehab 
1840aa77f6cSMauro Carvalho Chehab /* for firmware loading, fw_state */
1850aa77f6cSMauro Carvalho Chehab #define S2255_FW_NOTLOADED	0
1860aa77f6cSMauro Carvalho Chehab #define S2255_FW_LOADED_DSPWAIT	1
1870aa77f6cSMauro Carvalho Chehab #define S2255_FW_SUCCESS	2
1880aa77f6cSMauro Carvalho Chehab #define S2255_FW_FAILED		3
1890aa77f6cSMauro Carvalho Chehab #define S2255_FW_DISCONNECTING  4
1900aa77f6cSMauro Carvalho Chehab #define S2255_FW_MARKER		cpu_to_le32(0x22552f2f)
1910aa77f6cSMauro Carvalho Chehab /* 2255 read states */
1920aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE         0
1930aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME        1
1940aa77f6cSMauro Carvalho Chehab struct s2255_fw {
1950aa77f6cSMauro Carvalho Chehab 	int		      fw_loaded;
1960aa77f6cSMauro Carvalho Chehab 	int		      fw_size;
1970aa77f6cSMauro Carvalho Chehab 	struct urb	      *fw_urb;
1980aa77f6cSMauro Carvalho Chehab 	atomic_t	      fw_state;
1990aa77f6cSMauro Carvalho Chehab 	void		      *pfw_data;
2000aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t     wait_fw;
2010aa77f6cSMauro Carvalho Chehab 	const struct firmware *fw;
2020aa77f6cSMauro Carvalho Chehab };
2030aa77f6cSMauro Carvalho Chehab 
2040aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo {
2050aa77f6cSMauro Carvalho Chehab 	u32 max_transfer_size;
2060aa77f6cSMauro Carvalho Chehab 	u32 cur_transfer_size;
2070aa77f6cSMauro Carvalho Chehab 	u8 *transfer_buffer;
2080aa77f6cSMauro Carvalho Chehab 	u32 state;
2090aa77f6cSMauro Carvalho Chehab 	void *stream_urb;
2100aa77f6cSMauro Carvalho Chehab 	void *dev;	/* back pointer to s2255_dev struct*/
2110aa77f6cSMauro Carvalho Chehab 	u32 err_count;
2120aa77f6cSMauro Carvalho Chehab 	u32 idx;
2130aa77f6cSMauro Carvalho Chehab };
2140aa77f6cSMauro Carvalho Chehab 
2150aa77f6cSMauro Carvalho Chehab struct s2255_fmt; /*forward declaration */
2160aa77f6cSMauro Carvalho Chehab struct s2255_dev;
2170aa77f6cSMauro Carvalho Chehab 
2180aa77f6cSMauro Carvalho Chehab struct s2255_channel {
2190aa77f6cSMauro Carvalho Chehab 	struct video_device	vdev;
2200aa77f6cSMauro Carvalho Chehab 	int			resources;
2210aa77f6cSMauro Carvalho Chehab 	struct s2255_dmaqueue	vidq;
2220aa77f6cSMauro Carvalho Chehab 	struct s2255_bufferi	buffer;
2230aa77f6cSMauro Carvalho Chehab 	struct s2255_mode	mode;
2240aa77f6cSMauro Carvalho Chehab 	/* jpeg compression */
2250aa77f6cSMauro Carvalho Chehab 	struct v4l2_jpegcompression jc;
2260aa77f6cSMauro Carvalho Chehab 	/* capture parameters (for high quality mode full size) */
2270aa77f6cSMauro Carvalho Chehab 	struct v4l2_captureparm cap_parm;
2280aa77f6cSMauro Carvalho Chehab 	int			cur_frame;
2290aa77f6cSMauro Carvalho Chehab 	int			last_frame;
2300aa77f6cSMauro Carvalho Chehab 
2310aa77f6cSMauro Carvalho Chehab 	int			b_acquire;
2320aa77f6cSMauro Carvalho Chehab 	/* allocated image size */
2330aa77f6cSMauro Carvalho Chehab 	unsigned long		req_image_size;
2340aa77f6cSMauro Carvalho Chehab 	/* received packet size */
2350aa77f6cSMauro Carvalho Chehab 	unsigned long		pkt_size;
2360aa77f6cSMauro Carvalho Chehab 	int			bad_payload;
2370aa77f6cSMauro Carvalho Chehab 	unsigned long		frame_count;
2380aa77f6cSMauro Carvalho Chehab 	/* if JPEG image */
2390aa77f6cSMauro Carvalho Chehab 	int                     jpg_size;
2400aa77f6cSMauro Carvalho Chehab 	/* if channel configured to default state */
2410aa77f6cSMauro Carvalho Chehab 	int                     configured;
2420aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_setmode;
2430aa77f6cSMauro Carvalho Chehab 	int                     setmode_ready;
2440aa77f6cSMauro Carvalho Chehab 	/* video status items */
2450aa77f6cSMauro Carvalho Chehab 	int                     vidstatus;
2460aa77f6cSMauro Carvalho Chehab 	wait_queue_head_t       wait_vidstatus;
2470aa77f6cSMauro Carvalho Chehab 	int                     vidstatus_ready;
2480aa77f6cSMauro Carvalho Chehab 	unsigned int		width;
2490aa77f6cSMauro Carvalho Chehab 	unsigned int		height;
2500aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt	*fmt;
2510aa77f6cSMauro Carvalho Chehab 	int idx; /* channel number on device, 0-3 */
2520aa77f6cSMauro Carvalho Chehab };
2530aa77f6cSMauro Carvalho Chehab 
2540aa77f6cSMauro Carvalho Chehab 
2550aa77f6cSMauro Carvalho Chehab struct s2255_dev {
2560aa77f6cSMauro Carvalho Chehab 	struct s2255_channel    channel[MAX_CHANNELS];
2570aa77f6cSMauro Carvalho Chehab 	struct v4l2_device 	v4l2_dev;
2580aa77f6cSMauro Carvalho Chehab 	atomic_t                num_channels;
2590aa77f6cSMauro Carvalho Chehab 	int			frames;
2600aa77f6cSMauro Carvalho Chehab 	struct mutex		lock;	/* channels[].vdev.lock */
2610aa77f6cSMauro Carvalho Chehab 	struct usb_device	*udev;
2620aa77f6cSMauro Carvalho Chehab 	struct usb_interface	*interface;
2630aa77f6cSMauro Carvalho Chehab 	u8			read_endpoint;
2640aa77f6cSMauro Carvalho Chehab 	struct timer_list	timer;
2650aa77f6cSMauro Carvalho Chehab 	struct s2255_fw	*fw_data;
2660aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo	pipe;
2670aa77f6cSMauro Carvalho Chehab 	u32			cc;	/* current channel */
2680aa77f6cSMauro Carvalho Chehab 	int			frame_ready;
2690aa77f6cSMauro Carvalho Chehab 	int                     chn_ready;
2700aa77f6cSMauro Carvalho Chehab 	spinlock_t              slock;
2710aa77f6cSMauro Carvalho Chehab 	/* dsp firmware version (f2255usb.bin) */
2720aa77f6cSMauro Carvalho Chehab 	int                     dsp_fw_ver;
2730aa77f6cSMauro Carvalho Chehab 	u16                     pid; /* product id */
2740aa77f6cSMauro Carvalho Chehab };
2750aa77f6cSMauro Carvalho Chehab 
2760aa77f6cSMauro Carvalho Chehab static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
2770aa77f6cSMauro Carvalho Chehab {
2780aa77f6cSMauro Carvalho Chehab 	return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
2790aa77f6cSMauro Carvalho Chehab }
2800aa77f6cSMauro Carvalho Chehab 
2810aa77f6cSMauro Carvalho Chehab struct s2255_fmt {
2820aa77f6cSMauro Carvalho Chehab 	char *name;
2830aa77f6cSMauro Carvalho Chehab 	u32 fourcc;
2840aa77f6cSMauro Carvalho Chehab 	int depth;
2850aa77f6cSMauro Carvalho Chehab };
2860aa77f6cSMauro Carvalho Chehab 
2870aa77f6cSMauro Carvalho Chehab /* buffer for one video frame */
2880aa77f6cSMauro Carvalho Chehab struct s2255_buffer {
2890aa77f6cSMauro Carvalho Chehab 	/* common v4l buffer stuff -- must be first */
2900aa77f6cSMauro Carvalho Chehab 	struct videobuf_buffer vb;
2910aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
2920aa77f6cSMauro Carvalho Chehab };
2930aa77f6cSMauro Carvalho Chehab 
2940aa77f6cSMauro Carvalho Chehab struct s2255_fh {
2950aa77f6cSMauro Carvalho Chehab 	struct s2255_dev	*dev;
2960aa77f6cSMauro Carvalho Chehab 	struct videobuf_queue	vb_vidq;
2970aa77f6cSMauro Carvalho Chehab 	enum v4l2_buf_type	type;
2980aa77f6cSMauro Carvalho Chehab 	struct s2255_channel	*channel;
2990aa77f6cSMauro Carvalho Chehab 	int			resources;
3000aa77f6cSMauro Carvalho Chehab };
3010aa77f6cSMauro Carvalho Chehab 
3020aa77f6cSMauro Carvalho Chehab /* current cypress EEPROM firmware version */
3030aa77f6cSMauro Carvalho Chehab #define S2255_CUR_USB_FWVER	((3 << 8) | 12)
3040aa77f6cSMauro Carvalho Chehab /* current DSP FW version */
3050aa77f6cSMauro Carvalho Chehab #define S2255_CUR_DSP_FWVER     10104
3060aa77f6cSMauro Carvalho Chehab /* Need DSP version 5+ for video status feature */
3070aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_STATUS      5
3080aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_COLORFILTER 8
3090aa77f6cSMauro Carvalho Chehab #define S2255_NORMS		(V4L2_STD_PAL | V4L2_STD_NTSC)
3100aa77f6cSMauro Carvalho Chehab 
3110aa77f6cSMauro Carvalho Chehab /* private V4L2 controls */
3120aa77f6cSMauro Carvalho Chehab 
3130aa77f6cSMauro Carvalho Chehab /*
3140aa77f6cSMauro Carvalho Chehab  * The following chart displays how COLORFILTER should be set
3150aa77f6cSMauro Carvalho Chehab  *  =========================================================
3160aa77f6cSMauro Carvalho Chehab  *  =     fourcc              =     COLORFILTER             =
3170aa77f6cSMauro Carvalho Chehab  *  =                         ===============================
3180aa77f6cSMauro Carvalho Chehab  *  =                         =   0             =    1      =
3190aa77f6cSMauro Carvalho Chehab  *  =========================================================
3200aa77f6cSMauro Carvalho Chehab  *  =  V4L2_PIX_FMT_GREY(Y8)  = monochrome from = monochrome=
3210aa77f6cSMauro Carvalho Chehab  *  =                         = s-video or      = composite =
3220aa77f6cSMauro Carvalho Chehab  *  =                         = B/W camera      = input     =
3230aa77f6cSMauro Carvalho Chehab  *  =========================================================
3240aa77f6cSMauro Carvalho Chehab  *  =    other                = color, svideo   = color,    =
3250aa77f6cSMauro Carvalho Chehab  *  =                         =                 = composite =
3260aa77f6cSMauro Carvalho Chehab  *  =========================================================
3270aa77f6cSMauro Carvalho Chehab  *
3280aa77f6cSMauro Carvalho Chehab  * Notes:
3290aa77f6cSMauro Carvalho Chehab  *   channels 0-3 on 2255 are composite
3300aa77f6cSMauro Carvalho Chehab  *   channels 0-1 on 2257 are composite, 2-3 are s-video
3310aa77f6cSMauro Carvalho Chehab  * If COLORFILTER is 0 with a composite color camera connected,
3320aa77f6cSMauro Carvalho Chehab  * the output will appear monochrome but hatching
3330aa77f6cSMauro Carvalho Chehab  * will occur.
3340aa77f6cSMauro Carvalho Chehab  * COLORFILTER is different from "color killer" and "color effects"
3350aa77f6cSMauro Carvalho Chehab  * for reasons above.
3360aa77f6cSMauro Carvalho Chehab  */
3370aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_ON  1
3380aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_OFF 0
3390aa77f6cSMauro Carvalho Chehab #define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0)
3400aa77f6cSMauro Carvalho Chehab 
3410aa77f6cSMauro Carvalho Chehab /* frame prefix size (sent once every frame) */
3420aa77f6cSMauro Carvalho Chehab #define PREFIX_SIZE		512
3430aa77f6cSMauro Carvalho Chehab 
3440aa77f6cSMauro Carvalho Chehab /* Channels on box are in reverse order */
3450aa77f6cSMauro Carvalho Chehab static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
3460aa77f6cSMauro Carvalho Chehab 
3470aa77f6cSMauro Carvalho Chehab static int debug;
3480aa77f6cSMauro Carvalho Chehab static int *s2255_debug = &debug;
3490aa77f6cSMauro Carvalho Chehab 
3500aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev);
3510aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev);
3520aa77f6cSMauro Carvalho Chehab static int s2255_start_acquire(struct s2255_channel *channel);
3530aa77f6cSMauro Carvalho Chehab static int s2255_stop_acquire(struct s2255_channel *channel);
3540aa77f6cSMauro Carvalho Chehab static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf,
3550aa77f6cSMauro Carvalho Chehab 			   int jpgsize);
3560aa77f6cSMauro Carvalho Chehab static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode);
3570aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev);
3580aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset);
3590aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev);
3600aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
3610aa77f6cSMauro Carvalho Chehab 			     u16 index, u16 value, void *buf,
3620aa77f6cSMauro Carvalho Chehab 			     s32 buf_len, int bOut);
3630aa77f6cSMauro Carvalho Chehab 
3640aa77f6cSMauro Carvalho Chehab /* dev_err macro with driver name */
3650aa77f6cSMauro Carvalho Chehab #define S2255_DRIVER_NAME "s2255"
3660aa77f6cSMauro Carvalho Chehab #define s2255_dev_err(dev, fmt, arg...)					\
3670aa77f6cSMauro Carvalho Chehab 		dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
3680aa77f6cSMauro Carvalho Chehab 
3690aa77f6cSMauro Carvalho Chehab #define dprintk(level, fmt, arg...)					\
3700aa77f6cSMauro Carvalho Chehab 	do {								\
3710aa77f6cSMauro Carvalho Chehab 		if (*s2255_debug >= (level)) {				\
3720aa77f6cSMauro Carvalho Chehab 			printk(KERN_DEBUG S2255_DRIVER_NAME		\
3730aa77f6cSMauro Carvalho Chehab 				": " fmt, ##arg);			\
3740aa77f6cSMauro Carvalho Chehab 		}							\
3750aa77f6cSMauro Carvalho Chehab 	} while (0)
3760aa77f6cSMauro Carvalho Chehab 
3770aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver;
3780aa77f6cSMauro Carvalho Chehab 
3790aa77f6cSMauro Carvalho Chehab /* Declare static vars that will be used as parameters */
3800aa77f6cSMauro Carvalho Chehab static unsigned int vid_limit = 16;	/* Video memory limit, in Mb */
3810aa77f6cSMauro Carvalho Chehab 
3820aa77f6cSMauro Carvalho Chehab /* start video number */
3830aa77f6cSMauro Carvalho Chehab static int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
3840aa77f6cSMauro Carvalho Chehab 
3850aa77f6cSMauro Carvalho Chehab /* Enable jpeg capture. */
3860aa77f6cSMauro Carvalho Chehab static int jpeg_enable = 1;
3870aa77f6cSMauro Carvalho Chehab 
3880aa77f6cSMauro Carvalho Chehab module_param(debug, int, 0644);
3890aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
3900aa77f6cSMauro Carvalho Chehab module_param(vid_limit, int, 0644);
3910aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
3920aa77f6cSMauro Carvalho Chehab module_param(video_nr, int, 0644);
3930aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
3940aa77f6cSMauro Carvalho Chehab module_param(jpeg_enable, int, 0644);
3950aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
3960aa77f6cSMauro Carvalho Chehab 
3970aa77f6cSMauro Carvalho Chehab /* USB device table */
3980aa77f6cSMauro Carvalho Chehab #define USB_SENSORAY_VID	0x1943
3990aa77f6cSMauro Carvalho Chehab static struct usb_device_id s2255_table[] = {
4000aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
4010aa77f6cSMauro Carvalho Chehab 	{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
4020aa77f6cSMauro Carvalho Chehab 	{ }			/* Terminating entry */
4030aa77f6cSMauro Carvalho Chehab };
4040aa77f6cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, s2255_table);
4050aa77f6cSMauro Carvalho Chehab 
4060aa77f6cSMauro Carvalho Chehab #define BUFFER_TIMEOUT msecs_to_jiffies(400)
4070aa77f6cSMauro Carvalho Chehab 
4080aa77f6cSMauro Carvalho Chehab /* image formats.  */
4090aa77f6cSMauro Carvalho Chehab /* JPEG formats must be defined last to support jpeg_enable parameter */
4100aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt formats[] = {
4110aa77f6cSMauro Carvalho Chehab 	{
4120aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, planar, YUV422P",
4130aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_YUV422P,
4140aa77f6cSMauro Carvalho Chehab 		.depth = 16
4150aa77f6cSMauro Carvalho Chehab 
4160aa77f6cSMauro Carvalho Chehab 	}, {
4170aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, packed, YUYV",
4180aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_YUYV,
4190aa77f6cSMauro Carvalho Chehab 		.depth = 16
4200aa77f6cSMauro Carvalho Chehab 
4210aa77f6cSMauro Carvalho Chehab 	}, {
4220aa77f6cSMauro Carvalho Chehab 		.name = "4:2:2, packed, UYVY",
4230aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_UYVY,
4240aa77f6cSMauro Carvalho Chehab 		.depth = 16
4250aa77f6cSMauro Carvalho Chehab 	}, {
4260aa77f6cSMauro Carvalho Chehab 		.name = "8bpp GREY",
4270aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_GREY,
4280aa77f6cSMauro Carvalho Chehab 		.depth = 8
4290aa77f6cSMauro Carvalho Chehab 	}, {
4300aa77f6cSMauro Carvalho Chehab 		.name = "JPG",
4310aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_JPEG,
4320aa77f6cSMauro Carvalho Chehab 		.depth = 24
4330aa77f6cSMauro Carvalho Chehab 	}, {
4340aa77f6cSMauro Carvalho Chehab 		.name = "MJPG",
4350aa77f6cSMauro Carvalho Chehab 		.fourcc = V4L2_PIX_FMT_MJPEG,
4360aa77f6cSMauro Carvalho Chehab 		.depth = 24
4370aa77f6cSMauro Carvalho Chehab 	}
4380aa77f6cSMauro Carvalho Chehab };
4390aa77f6cSMauro Carvalho Chehab 
4400aa77f6cSMauro Carvalho Chehab static int norm_maxw(struct video_device *vdev)
4410aa77f6cSMauro Carvalho Chehab {
4420aa77f6cSMauro Carvalho Chehab 	return (vdev->current_norm & V4L2_STD_NTSC) ?
4430aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
4440aa77f6cSMauro Carvalho Chehab }
4450aa77f6cSMauro Carvalho Chehab 
4460aa77f6cSMauro Carvalho Chehab static int norm_maxh(struct video_device *vdev)
4470aa77f6cSMauro Carvalho Chehab {
4480aa77f6cSMauro Carvalho Chehab 	return (vdev->current_norm & V4L2_STD_NTSC) ?
4490aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
4500aa77f6cSMauro Carvalho Chehab }
4510aa77f6cSMauro Carvalho Chehab 
4520aa77f6cSMauro Carvalho Chehab static int norm_minw(struct video_device *vdev)
4530aa77f6cSMauro Carvalho Chehab {
4540aa77f6cSMauro Carvalho Chehab 	return (vdev->current_norm & V4L2_STD_NTSC) ?
4550aa77f6cSMauro Carvalho Chehab 	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
4560aa77f6cSMauro Carvalho Chehab }
4570aa77f6cSMauro Carvalho Chehab 
4580aa77f6cSMauro Carvalho Chehab static int norm_minh(struct video_device *vdev)
4590aa77f6cSMauro Carvalho Chehab {
4600aa77f6cSMauro Carvalho Chehab 	return (vdev->current_norm & V4L2_STD_NTSC) ?
4610aa77f6cSMauro Carvalho Chehab 	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
4620aa77f6cSMauro Carvalho Chehab }
4630aa77f6cSMauro Carvalho Chehab 
4640aa77f6cSMauro Carvalho Chehab 
4650aa77f6cSMauro Carvalho Chehab /*
4660aa77f6cSMauro Carvalho Chehab  * TODO: fixme: move YUV reordering to hardware
4670aa77f6cSMauro Carvalho Chehab  * converts 2255 planar format to yuyv or uyvy
4680aa77f6cSMauro Carvalho Chehab  */
4690aa77f6cSMauro Carvalho Chehab static void planar422p_to_yuv_packed(const unsigned char *in,
4700aa77f6cSMauro Carvalho Chehab 				     unsigned char *out,
4710aa77f6cSMauro Carvalho Chehab 				     int width, int height,
4720aa77f6cSMauro Carvalho Chehab 				     int fmt)
4730aa77f6cSMauro Carvalho Chehab {
4740aa77f6cSMauro Carvalho Chehab 	unsigned char *pY;
4750aa77f6cSMauro Carvalho Chehab 	unsigned char *pCb;
4760aa77f6cSMauro Carvalho Chehab 	unsigned char *pCr;
4770aa77f6cSMauro Carvalho Chehab 	unsigned long size = height * width;
4780aa77f6cSMauro Carvalho Chehab 	unsigned int i;
4790aa77f6cSMauro Carvalho Chehab 	pY = (unsigned char *)in;
4800aa77f6cSMauro Carvalho Chehab 	pCr = (unsigned char *)in + height * width;
4810aa77f6cSMauro Carvalho Chehab 	pCb = (unsigned char *)in + height * width + (height * width / 2);
4820aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < size * 2; i += 4) {
4830aa77f6cSMauro Carvalho Chehab 		out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
4840aa77f6cSMauro Carvalho Chehab 		out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
4850aa77f6cSMauro Carvalho Chehab 		out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
4860aa77f6cSMauro Carvalho Chehab 		out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
4870aa77f6cSMauro Carvalho Chehab 	}
4880aa77f6cSMauro Carvalho Chehab 	return;
4890aa77f6cSMauro Carvalho Chehab }
4900aa77f6cSMauro Carvalho Chehab 
4910aa77f6cSMauro Carvalho Chehab static void s2255_reset_dsppower(struct s2255_dev *dev)
4920aa77f6cSMauro Carvalho Chehab {
4930aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
4940aa77f6cSMauro Carvalho Chehab 	msleep(10);
4950aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
4960aa77f6cSMauro Carvalho Chehab 	msleep(600);
4970aa77f6cSMauro Carvalho Chehab 	s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
4980aa77f6cSMauro Carvalho Chehab 	return;
4990aa77f6cSMauro Carvalho Chehab }
5000aa77f6cSMauro Carvalho Chehab 
5010aa77f6cSMauro Carvalho Chehab /* kickstarts the firmware loading. from probe
5020aa77f6cSMauro Carvalho Chehab  */
5030aa77f6cSMauro Carvalho Chehab static void s2255_timer(unsigned long user_data)
5040aa77f6cSMauro Carvalho Chehab {
5050aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = (struct s2255_fw *)user_data;
5060aa77f6cSMauro Carvalho Chehab 	dprintk(100, "%s\n", __func__);
5070aa77f6cSMauro Carvalho Chehab 	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
5080aa77f6cSMauro Carvalho Chehab 		printk(KERN_ERR "s2255: can't submit urb\n");
5090aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5100aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5110aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5120aa77f6cSMauro Carvalho Chehab 		return;
5130aa77f6cSMauro Carvalho Chehab 	}
5140aa77f6cSMauro Carvalho Chehab }
5150aa77f6cSMauro Carvalho Chehab 
5160aa77f6cSMauro Carvalho Chehab 
5170aa77f6cSMauro Carvalho Chehab /* this loads the firmware asynchronously.
5180aa77f6cSMauro Carvalho Chehab    Originally this was done synchroously in probe.
5190aa77f6cSMauro Carvalho Chehab    But it is better to load it asynchronously here than block
5200aa77f6cSMauro Carvalho Chehab    inside the probe function. Blocking inside probe affects boot time.
5210aa77f6cSMauro Carvalho Chehab    FW loading is triggered by the timer in the probe function
5220aa77f6cSMauro Carvalho Chehab */
5230aa77f6cSMauro Carvalho Chehab static void s2255_fwchunk_complete(struct urb *urb)
5240aa77f6cSMauro Carvalho Chehab {
5250aa77f6cSMauro Carvalho Chehab 	struct s2255_fw *data = urb->context;
5260aa77f6cSMauro Carvalho Chehab 	struct usb_device *udev = urb->dev;
5270aa77f6cSMauro Carvalho Chehab 	int len;
5280aa77f6cSMauro Carvalho Chehab 	dprintk(100, "%s: udev %p urb %p", __func__, udev, urb);
5290aa77f6cSMauro Carvalho Chehab 	if (urb->status) {
5300aa77f6cSMauro Carvalho Chehab 		dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
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 	if (data->fw_urb == NULL) {
5370aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&udev->dev, "disconnected\n");
5380aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_FAILED);
5390aa77f6cSMauro Carvalho Chehab 		/* wake up anything waiting for the firmware */
5400aa77f6cSMauro Carvalho Chehab 		wake_up(&data->wait_fw);
5410aa77f6cSMauro Carvalho Chehab 		return;
5420aa77f6cSMauro Carvalho Chehab 	}
5430aa77f6cSMauro Carvalho Chehab #define CHUNK_SIZE 512
5440aa77f6cSMauro Carvalho Chehab 	/* all USB transfers must be done with continuous kernel memory.
5450aa77f6cSMauro Carvalho Chehab 	   can't allocate more than 128k in current linux kernel, so
5460aa77f6cSMauro Carvalho Chehab 	   upload the firmware in chunks
5470aa77f6cSMauro Carvalho Chehab 	 */
5480aa77f6cSMauro Carvalho Chehab 	if (data->fw_loaded < data->fw_size) {
5490aa77f6cSMauro Carvalho Chehab 		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
5500aa77f6cSMauro Carvalho Chehab 		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
5510aa77f6cSMauro Carvalho Chehab 
5520aa77f6cSMauro Carvalho Chehab 		if (len < CHUNK_SIZE)
5530aa77f6cSMauro Carvalho Chehab 			memset(data->pfw_data, 0, CHUNK_SIZE);
5540aa77f6cSMauro Carvalho Chehab 
5550aa77f6cSMauro Carvalho Chehab 		dprintk(100, "completed len %d, loaded %d \n", len,
5560aa77f6cSMauro Carvalho Chehab 			data->fw_loaded);
5570aa77f6cSMauro Carvalho Chehab 
5580aa77f6cSMauro Carvalho Chehab 		memcpy(data->pfw_data,
5590aa77f6cSMauro Carvalho Chehab 		       (char *) data->fw->data + data->fw_loaded, len);
5600aa77f6cSMauro Carvalho Chehab 
5610aa77f6cSMauro Carvalho Chehab 		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
5620aa77f6cSMauro Carvalho Chehab 				  data->pfw_data, CHUNK_SIZE,
5630aa77f6cSMauro Carvalho Chehab 				  s2255_fwchunk_complete, data);
5640aa77f6cSMauro Carvalho Chehab 		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
5650aa77f6cSMauro Carvalho Chehab 			dev_err(&udev->dev, "failed submit URB\n");
5660aa77f6cSMauro Carvalho Chehab 			atomic_set(&data->fw_state, S2255_FW_FAILED);
5670aa77f6cSMauro Carvalho Chehab 			/* wake up anything waiting for the firmware */
5680aa77f6cSMauro Carvalho Chehab 			wake_up(&data->wait_fw);
5690aa77f6cSMauro Carvalho Chehab 			return;
5700aa77f6cSMauro Carvalho Chehab 		}
5710aa77f6cSMauro Carvalho Chehab 		data->fw_loaded += len;
5720aa77f6cSMauro Carvalho Chehab 	} else {
5730aa77f6cSMauro Carvalho Chehab 		atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
5740aa77f6cSMauro Carvalho Chehab 		dprintk(100, "%s: firmware upload complete\n", __func__);
5750aa77f6cSMauro Carvalho Chehab 	}
5760aa77f6cSMauro Carvalho Chehab 	return;
5770aa77f6cSMauro Carvalho Chehab 
5780aa77f6cSMauro Carvalho Chehab }
5790aa77f6cSMauro Carvalho Chehab 
5800aa77f6cSMauro Carvalho Chehab static int s2255_got_frame(struct s2255_channel *channel, int jpgsize)
5810aa77f6cSMauro Carvalho Chehab {
5820aa77f6cSMauro Carvalho Chehab 	struct s2255_dmaqueue *dma_q = &channel->vidq;
5830aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf;
5840aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
5850aa77f6cSMauro Carvalho Chehab 	unsigned long flags = 0;
5860aa77f6cSMauro Carvalho Chehab 	int rc = 0;
5870aa77f6cSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->slock, flags);
5880aa77f6cSMauro Carvalho Chehab 	if (list_empty(&dma_q->active)) {
5890aa77f6cSMauro Carvalho Chehab 		dprintk(1, "No active queue to serve\n");
5900aa77f6cSMauro Carvalho Chehab 		rc = -1;
5910aa77f6cSMauro Carvalho Chehab 		goto unlock;
5920aa77f6cSMauro Carvalho Chehab 	}
5930aa77f6cSMauro Carvalho Chehab 	buf = list_entry(dma_q->active.next,
5940aa77f6cSMauro Carvalho Chehab 			 struct s2255_buffer, vb.queue);
5950aa77f6cSMauro Carvalho Chehab 	list_del(&buf->vb.queue);
5960aa77f6cSMauro Carvalho Chehab 	do_gettimeofday(&buf->vb.ts);
5970aa77f6cSMauro Carvalho Chehab 	s2255_fillbuff(channel, buf, jpgsize);
5980aa77f6cSMauro Carvalho Chehab 	wake_up(&buf->vb.done);
5990aa77f6cSMauro Carvalho Chehab 	dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
6000aa77f6cSMauro Carvalho Chehab unlock:
6010aa77f6cSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->slock, flags);
6020aa77f6cSMauro Carvalho Chehab 	return rc;
6030aa77f6cSMauro Carvalho Chehab }
6040aa77f6cSMauro Carvalho Chehab 
6050aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt *format_by_fourcc(int fourcc)
6060aa77f6cSMauro Carvalho Chehab {
6070aa77f6cSMauro Carvalho Chehab 	unsigned int i;
6080aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
6090aa77f6cSMauro Carvalho Chehab 		if (-1 == formats[i].fourcc)
6100aa77f6cSMauro Carvalho Chehab 			continue;
6110aa77f6cSMauro Carvalho Chehab 	if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
6120aa77f6cSMauro Carvalho Chehab 			     (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
6130aa77f6cSMauro Carvalho Chehab 	    continue;
6140aa77f6cSMauro Carvalho Chehab 		if (formats[i].fourcc == fourcc)
6150aa77f6cSMauro Carvalho Chehab 			return formats + i;
6160aa77f6cSMauro Carvalho Chehab 	}
6170aa77f6cSMauro Carvalho Chehab 	return NULL;
6180aa77f6cSMauro Carvalho Chehab }
6190aa77f6cSMauro Carvalho Chehab 
6200aa77f6cSMauro Carvalho Chehab /* video buffer vmalloc implementation based partly on VIVI driver which is
6210aa77f6cSMauro Carvalho Chehab  *          Copyright (c) 2006 by
6220aa77f6cSMauro Carvalho Chehab  *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
6230aa77f6cSMauro Carvalho Chehab  *                  Ted Walther <ted--a.t--enumera.com>
6240aa77f6cSMauro Carvalho Chehab  *                  John Sokol <sokol--a.t--videotechnology.com>
6250aa77f6cSMauro Carvalho Chehab  *                  http://v4l.videotechnology.com/
6260aa77f6cSMauro Carvalho Chehab  *
6270aa77f6cSMauro Carvalho Chehab  */
6280aa77f6cSMauro Carvalho Chehab static void s2255_fillbuff(struct s2255_channel *channel,
6290aa77f6cSMauro Carvalho Chehab 			   struct s2255_buffer *buf, int jpgsize)
6300aa77f6cSMauro Carvalho Chehab {
6310aa77f6cSMauro Carvalho Chehab 	int pos = 0;
6320aa77f6cSMauro Carvalho Chehab 	struct timeval ts;
6330aa77f6cSMauro Carvalho Chehab 	const char *tmpbuf;
6340aa77f6cSMauro Carvalho Chehab 	char *vbuf = videobuf_to_vmalloc(&buf->vb);
6350aa77f6cSMauro Carvalho Chehab 	unsigned long last_frame;
6360aa77f6cSMauro Carvalho Chehab 
6370aa77f6cSMauro Carvalho Chehab 	if (!vbuf)
6380aa77f6cSMauro Carvalho Chehab 		return;
6390aa77f6cSMauro Carvalho Chehab 	last_frame = channel->last_frame;
6400aa77f6cSMauro Carvalho Chehab 	if (last_frame != -1) {
6410aa77f6cSMauro Carvalho Chehab 		tmpbuf =
6420aa77f6cSMauro Carvalho Chehab 		    (const char *)channel->buffer.frame[last_frame].lpvbits;
6430aa77f6cSMauro Carvalho Chehab 		switch (buf->fmt->fourcc) {
6440aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUYV:
6450aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_UYVY:
6460aa77f6cSMauro Carvalho Chehab 			planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
6470aa77f6cSMauro Carvalho Chehab 						 vbuf, buf->vb.width,
6480aa77f6cSMauro Carvalho Chehab 						 buf->vb.height,
6490aa77f6cSMauro Carvalho Chehab 						 buf->fmt->fourcc);
6500aa77f6cSMauro Carvalho Chehab 			break;
6510aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_GREY:
6520aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
6530aa77f6cSMauro Carvalho Chehab 			break;
6540aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_JPEG:
6550aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_MJPEG:
6560aa77f6cSMauro Carvalho Chehab 			buf->vb.size = jpgsize;
6570aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf, buf->vb.size);
6580aa77f6cSMauro Carvalho Chehab 			break;
6590aa77f6cSMauro Carvalho Chehab 		case V4L2_PIX_FMT_YUV422P:
6600aa77f6cSMauro Carvalho Chehab 			memcpy(vbuf, tmpbuf,
6610aa77f6cSMauro Carvalho Chehab 			       buf->vb.width * buf->vb.height * 2);
6620aa77f6cSMauro Carvalho Chehab 			break;
6630aa77f6cSMauro Carvalho Chehab 		default:
6640aa77f6cSMauro Carvalho Chehab 			printk(KERN_DEBUG "s2255: unknown format?\n");
6650aa77f6cSMauro Carvalho Chehab 		}
6660aa77f6cSMauro Carvalho Chehab 		channel->last_frame = -1;
6670aa77f6cSMauro Carvalho Chehab 	} else {
6680aa77f6cSMauro Carvalho Chehab 		printk(KERN_ERR "s2255: =======no frame\n");
6690aa77f6cSMauro Carvalho Chehab 		return;
6700aa77f6cSMauro Carvalho Chehab 
6710aa77f6cSMauro Carvalho Chehab 	}
6720aa77f6cSMauro Carvalho Chehab 	dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
6730aa77f6cSMauro Carvalho Chehab 		(unsigned long)vbuf, pos);
6740aa77f6cSMauro Carvalho Chehab 	/* tell v4l buffer was filled */
6750aa77f6cSMauro Carvalho Chehab 
6760aa77f6cSMauro Carvalho Chehab 	buf->vb.field_count = channel->frame_count * 2;
6770aa77f6cSMauro Carvalho Chehab 	do_gettimeofday(&ts);
6780aa77f6cSMauro Carvalho Chehab 	buf->vb.ts = ts;
6790aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_DONE;
6800aa77f6cSMauro Carvalho Chehab }
6810aa77f6cSMauro Carvalho Chehab 
6820aa77f6cSMauro Carvalho Chehab 
6830aa77f6cSMauro Carvalho Chehab /* ------------------------------------------------------------------
6840aa77f6cSMauro Carvalho Chehab    Videobuf operations
6850aa77f6cSMauro Carvalho Chehab    ------------------------------------------------------------------*/
6860aa77f6cSMauro Carvalho Chehab 
6870aa77f6cSMauro Carvalho Chehab static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
6880aa77f6cSMauro Carvalho Chehab 			unsigned int *size)
6890aa77f6cSMauro Carvalho Chehab {
6900aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
6910aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
6920aa77f6cSMauro Carvalho Chehab 	*size = channel->width * channel->height * (channel->fmt->depth >> 3);
6930aa77f6cSMauro Carvalho Chehab 
6940aa77f6cSMauro Carvalho Chehab 	if (0 == *count)
6950aa77f6cSMauro Carvalho Chehab 		*count = S2255_DEF_BUFS;
6960aa77f6cSMauro Carvalho Chehab 
6970aa77f6cSMauro Carvalho Chehab 	if (*size * *count > vid_limit * 1024 * 1024)
6980aa77f6cSMauro Carvalho Chehab 		*count = (vid_limit * 1024 * 1024) / *size;
6990aa77f6cSMauro Carvalho Chehab 
7000aa77f6cSMauro Carvalho Chehab 	return 0;
7010aa77f6cSMauro Carvalho Chehab }
7020aa77f6cSMauro Carvalho Chehab 
7030aa77f6cSMauro Carvalho Chehab static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
7040aa77f6cSMauro Carvalho Chehab {
7050aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s\n", __func__);
7060aa77f6cSMauro Carvalho Chehab 
7070aa77f6cSMauro Carvalho Chehab 	videobuf_vmalloc_free(&buf->vb);
7080aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_NEEDS_INIT;
7090aa77f6cSMauro Carvalho Chehab }
7100aa77f6cSMauro Carvalho Chehab 
7110aa77f6cSMauro Carvalho Chehab static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
7120aa77f6cSMauro Carvalho Chehab 			  enum v4l2_field field)
7130aa77f6cSMauro Carvalho Chehab {
7140aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
7150aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
7160aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
7170aa77f6cSMauro Carvalho Chehab 	int rc;
7180aa77f6cSMauro Carvalho Chehab 	int w = channel->width;
7190aa77f6cSMauro Carvalho Chehab 	int h = channel->height;
7200aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s, field=%d\n", __func__, field);
7210aa77f6cSMauro Carvalho Chehab 	if (channel->fmt == NULL)
7220aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7230aa77f6cSMauro Carvalho Chehab 
7240aa77f6cSMauro Carvalho Chehab 	if ((w < norm_minw(&channel->vdev)) ||
7250aa77f6cSMauro Carvalho Chehab 	    (w > norm_maxw(&channel->vdev)) ||
7260aa77f6cSMauro Carvalho Chehab 	    (h < norm_minh(&channel->vdev)) ||
7270aa77f6cSMauro Carvalho Chehab 	    (h > norm_maxh(&channel->vdev))) {
7280aa77f6cSMauro Carvalho Chehab 		dprintk(4, "invalid buffer prepare\n");
7290aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7300aa77f6cSMauro Carvalho Chehab 	}
7310aa77f6cSMauro Carvalho Chehab 	buf->vb.size = w * h * (channel->fmt->depth >> 3);
7320aa77f6cSMauro Carvalho Chehab 	if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
7330aa77f6cSMauro Carvalho Chehab 		dprintk(4, "invalid buffer prepare\n");
7340aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
7350aa77f6cSMauro Carvalho Chehab 	}
7360aa77f6cSMauro Carvalho Chehab 
7370aa77f6cSMauro Carvalho Chehab 	buf->fmt = channel->fmt;
7380aa77f6cSMauro Carvalho Chehab 	buf->vb.width = w;
7390aa77f6cSMauro Carvalho Chehab 	buf->vb.height = h;
7400aa77f6cSMauro Carvalho Chehab 	buf->vb.field = field;
7410aa77f6cSMauro Carvalho Chehab 
7420aa77f6cSMauro Carvalho Chehab 	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
7430aa77f6cSMauro Carvalho Chehab 		rc = videobuf_iolock(vq, &buf->vb, NULL);
7440aa77f6cSMauro Carvalho Chehab 		if (rc < 0)
7450aa77f6cSMauro Carvalho Chehab 			goto fail;
7460aa77f6cSMauro Carvalho Chehab 	}
7470aa77f6cSMauro Carvalho Chehab 
7480aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_PREPARED;
7490aa77f6cSMauro Carvalho Chehab 	return 0;
7500aa77f6cSMauro Carvalho Chehab fail:
7510aa77f6cSMauro Carvalho Chehab 	free_buffer(vq, buf);
7520aa77f6cSMauro Carvalho Chehab 	return rc;
7530aa77f6cSMauro Carvalho Chehab }
7540aa77f6cSMauro Carvalho Chehab 
7550aa77f6cSMauro Carvalho Chehab static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
7560aa77f6cSMauro Carvalho Chehab {
7570aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
7580aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
7590aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
7600aa77f6cSMauro Carvalho Chehab 	struct s2255_dmaqueue *vidq = &channel->vidq;
7610aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s\n", __func__);
7620aa77f6cSMauro Carvalho Chehab 	buf->vb.state = VIDEOBUF_QUEUED;
7630aa77f6cSMauro Carvalho Chehab 	list_add_tail(&buf->vb.queue, &vidq->active);
7640aa77f6cSMauro Carvalho Chehab }
7650aa77f6cSMauro Carvalho Chehab 
7660aa77f6cSMauro Carvalho Chehab static void buffer_release(struct videobuf_queue *vq,
7670aa77f6cSMauro Carvalho Chehab 			   struct videobuf_buffer *vb)
7680aa77f6cSMauro Carvalho Chehab {
7690aa77f6cSMauro Carvalho Chehab 	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
7700aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = vq->priv_data;
7710aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s %d\n", __func__, fh->channel->idx);
7720aa77f6cSMauro Carvalho Chehab 	free_buffer(vq, buf);
7730aa77f6cSMauro Carvalho Chehab }
7740aa77f6cSMauro Carvalho Chehab 
7750aa77f6cSMauro Carvalho Chehab static struct videobuf_queue_ops s2255_video_qops = {
7760aa77f6cSMauro Carvalho Chehab 	.buf_setup = buffer_setup,
7770aa77f6cSMauro Carvalho Chehab 	.buf_prepare = buffer_prepare,
7780aa77f6cSMauro Carvalho Chehab 	.buf_queue = buffer_queue,
7790aa77f6cSMauro Carvalho Chehab 	.buf_release = buffer_release,
7800aa77f6cSMauro Carvalho Chehab };
7810aa77f6cSMauro Carvalho Chehab 
7820aa77f6cSMauro Carvalho Chehab 
7830aa77f6cSMauro Carvalho Chehab static int res_get(struct s2255_fh *fh)
7840aa77f6cSMauro Carvalho Chehab {
7850aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
7860aa77f6cSMauro Carvalho Chehab 	/* is it free? */
7870aa77f6cSMauro Carvalho Chehab 	if (channel->resources)
7880aa77f6cSMauro Carvalho Chehab 		return 0; /* no, someone else uses it */
7890aa77f6cSMauro Carvalho Chehab 	/* it's free, grab it */
7900aa77f6cSMauro Carvalho Chehab 	channel->resources = 1;
7910aa77f6cSMauro Carvalho Chehab 	fh->resources = 1;
7920aa77f6cSMauro Carvalho Chehab 	dprintk(1, "s2255: res: get\n");
7930aa77f6cSMauro Carvalho Chehab 	return 1;
7940aa77f6cSMauro Carvalho Chehab }
7950aa77f6cSMauro Carvalho Chehab 
7960aa77f6cSMauro Carvalho Chehab static int res_locked(struct s2255_fh *fh)
7970aa77f6cSMauro Carvalho Chehab {
7980aa77f6cSMauro Carvalho Chehab 	return fh->channel->resources;
7990aa77f6cSMauro Carvalho Chehab }
8000aa77f6cSMauro Carvalho Chehab 
8010aa77f6cSMauro Carvalho Chehab static int res_check(struct s2255_fh *fh)
8020aa77f6cSMauro Carvalho Chehab {
8030aa77f6cSMauro Carvalho Chehab 	return fh->resources;
8040aa77f6cSMauro Carvalho Chehab }
8050aa77f6cSMauro Carvalho Chehab 
8060aa77f6cSMauro Carvalho Chehab 
8070aa77f6cSMauro Carvalho Chehab static void res_free(struct s2255_fh *fh)
8080aa77f6cSMauro Carvalho Chehab {
8090aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
8100aa77f6cSMauro Carvalho Chehab 	channel->resources = 0;
8110aa77f6cSMauro Carvalho Chehab 	fh->resources = 0;
8120aa77f6cSMauro Carvalho Chehab 	dprintk(1, "res: put\n");
8130aa77f6cSMauro Carvalho Chehab }
8140aa77f6cSMauro Carvalho Chehab 
8150aa77f6cSMauro Carvalho Chehab static int vidioc_querymenu(struct file *file, void *priv,
8160aa77f6cSMauro Carvalho Chehab 			    struct v4l2_querymenu *qmenu)
8170aa77f6cSMauro Carvalho Chehab {
8180aa77f6cSMauro Carvalho Chehab 	static const char *colorfilter[] = {
8190aa77f6cSMauro Carvalho Chehab 		"Off",
8200aa77f6cSMauro Carvalho Chehab 		"On",
8210aa77f6cSMauro Carvalho Chehab 		NULL
8220aa77f6cSMauro Carvalho Chehab 	};
8230aa77f6cSMauro Carvalho Chehab 	if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) {
8240aa77f6cSMauro Carvalho Chehab 		int i;
8250aa77f6cSMauro Carvalho Chehab 		const char **menu_items = colorfilter;
8260aa77f6cSMauro Carvalho Chehab 		for (i = 0; i < qmenu->index && menu_items[i]; i++)
8270aa77f6cSMauro Carvalho Chehab 			; /* do nothing (from v4l2-common.c) */
8280aa77f6cSMauro Carvalho Chehab 		if (menu_items[i] == NULL || menu_items[i][0] == '\0')
8290aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
8300aa77f6cSMauro Carvalho Chehab 		strlcpy(qmenu->name, menu_items[qmenu->index],
8310aa77f6cSMauro Carvalho Chehab 			sizeof(qmenu->name));
8320aa77f6cSMauro Carvalho Chehab 		return 0;
8330aa77f6cSMauro Carvalho Chehab 	}
8340aa77f6cSMauro Carvalho Chehab 	return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
8350aa77f6cSMauro Carvalho Chehab }
8360aa77f6cSMauro Carvalho Chehab 
8370aa77f6cSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv,
8380aa77f6cSMauro Carvalho Chehab 			   struct v4l2_capability *cap)
8390aa77f6cSMauro Carvalho Chehab {
8400aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
8410aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
8420aa77f6cSMauro Carvalho Chehab 	strlcpy(cap->driver, "s2255", sizeof(cap->driver));
8430aa77f6cSMauro Carvalho Chehab 	strlcpy(cap->card, "s2255", sizeof(cap->card));
8440aa77f6cSMauro Carvalho Chehab 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
8450aa77f6cSMauro Carvalho Chehab 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
8460aa77f6cSMauro Carvalho Chehab 	return 0;
8470aa77f6cSMauro Carvalho Chehab }
8480aa77f6cSMauro Carvalho Chehab 
8490aa77f6cSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
8500aa77f6cSMauro Carvalho Chehab 			       struct v4l2_fmtdesc *f)
8510aa77f6cSMauro Carvalho Chehab {
8520aa77f6cSMauro Carvalho Chehab 	int index = f->index;
8530aa77f6cSMauro Carvalho Chehab 
8540aa77f6cSMauro Carvalho Chehab 	if (index >= ARRAY_SIZE(formats))
8550aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8560aa77f6cSMauro Carvalho Chehab 	if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
8570aa77f6cSMauro Carvalho Chehab 			(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
8580aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8590aa77f6cSMauro Carvalho Chehab 	dprintk(4, "name %s\n", formats[index].name);
8600aa77f6cSMauro Carvalho Chehab 	strlcpy(f->description, formats[index].name, sizeof(f->description));
8610aa77f6cSMauro Carvalho Chehab 	f->pixelformat = formats[index].fourcc;
8620aa77f6cSMauro Carvalho Chehab 	return 0;
8630aa77f6cSMauro Carvalho Chehab }
8640aa77f6cSMauro Carvalho Chehab 
8650aa77f6cSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
8660aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
8670aa77f6cSMauro Carvalho Chehab {
8680aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
8690aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
8700aa77f6cSMauro Carvalho Chehab 
8710aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.width = channel->width;
8720aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.height = channel->height;
8730aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.field = fh->vb_vidq.field;
8740aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.pixelformat = channel->fmt->fourcc;
8750aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3);
8760aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
8770aa77f6cSMauro Carvalho Chehab 	return 0;
8780aa77f6cSMauro Carvalho Chehab }
8790aa77f6cSMauro Carvalho Chehab 
8800aa77f6cSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
8810aa77f6cSMauro Carvalho Chehab 			      struct v4l2_format *f)
8820aa77f6cSMauro Carvalho Chehab {
8830aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
8840aa77f6cSMauro Carvalho Chehab 	enum v4l2_field field;
8850aa77f6cSMauro Carvalho Chehab 	int  b_any_field = 0;
8860aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
8870aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
8880aa77f6cSMauro Carvalho Chehab 	int is_ntsc;
8890aa77f6cSMauro Carvalho Chehab 	is_ntsc =
8900aa77f6cSMauro Carvalho Chehab 		(channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0;
8910aa77f6cSMauro Carvalho Chehab 
8920aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
8930aa77f6cSMauro Carvalho Chehab 
8940aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
8950aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
8960aa77f6cSMauro Carvalho Chehab 
8970aa77f6cSMauro Carvalho Chehab 	field = f->fmt.pix.field;
8980aa77f6cSMauro Carvalho Chehab 	if (field == V4L2_FIELD_ANY)
8990aa77f6cSMauro Carvalho Chehab 		b_any_field = 1;
9000aa77f6cSMauro Carvalho Chehab 
9010aa77f6cSMauro Carvalho Chehab 	dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n",
9020aa77f6cSMauro Carvalho Chehab 		__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
9030aa77f6cSMauro Carvalho Chehab 	if (is_ntsc) {
9040aa77f6cSMauro Carvalho Chehab 		/* NTSC */
9050aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
9060aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
9070aa77f6cSMauro Carvalho Chehab 			if (b_any_field) {
9080aa77f6cSMauro Carvalho Chehab 				field = V4L2_FIELD_SEQ_TB;
9090aa77f6cSMauro Carvalho Chehab 			} else if (!((field == V4L2_FIELD_INTERLACED) ||
9100aa77f6cSMauro Carvalho Chehab 				      (field == V4L2_FIELD_SEQ_TB) ||
9110aa77f6cSMauro Carvalho Chehab 				      (field == V4L2_FIELD_INTERLACED_TB))) {
9120aa77f6cSMauro Carvalho Chehab 				dprintk(1, "unsupported field setting\n");
9130aa77f6cSMauro Carvalho Chehab 				return -EINVAL;
9140aa77f6cSMauro Carvalho Chehab 			}
9150aa77f6cSMauro Carvalho Chehab 		} else {
9160aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
9170aa77f6cSMauro Carvalho Chehab 			if (b_any_field) {
9180aa77f6cSMauro Carvalho Chehab 				field = V4L2_FIELD_TOP;
9190aa77f6cSMauro Carvalho Chehab 			} else if (!((field == V4L2_FIELD_TOP) ||
9200aa77f6cSMauro Carvalho Chehab 				      (field == V4L2_FIELD_BOTTOM))) {
9210aa77f6cSMauro Carvalho Chehab 				dprintk(1, "unsupported field setting\n");
9220aa77f6cSMauro Carvalho Chehab 				return -EINVAL;
9230aa77f6cSMauro Carvalho Chehab 			}
9240aa77f6cSMauro Carvalho Chehab 
9250aa77f6cSMauro Carvalho Chehab 		}
9260aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
9270aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
9280aa77f6cSMauro Carvalho Chehab 		else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
9290aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
9300aa77f6cSMauro Carvalho Chehab 		else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
9310aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
9320aa77f6cSMauro Carvalho Chehab 		else
9330aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
9340aa77f6cSMauro Carvalho Chehab 	} else {
9350aa77f6cSMauro Carvalho Chehab 		/* PAL */
9360aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
9370aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
9380aa77f6cSMauro Carvalho Chehab 			if (b_any_field) {
9390aa77f6cSMauro Carvalho Chehab 				field = V4L2_FIELD_SEQ_TB;
9400aa77f6cSMauro Carvalho Chehab 			} else if (!((field == V4L2_FIELD_INTERLACED) ||
9410aa77f6cSMauro Carvalho Chehab 				      (field == V4L2_FIELD_SEQ_TB) ||
9420aa77f6cSMauro Carvalho Chehab 				      (field == V4L2_FIELD_INTERLACED_TB))) {
9430aa77f6cSMauro Carvalho Chehab 				dprintk(1, "unsupported field setting\n");
9440aa77f6cSMauro Carvalho Chehab 				return -EINVAL;
9450aa77f6cSMauro Carvalho Chehab 			}
9460aa77f6cSMauro Carvalho Chehab 		} else {
9470aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
9480aa77f6cSMauro Carvalho Chehab 			if (b_any_field) {
9490aa77f6cSMauro Carvalho Chehab 				field = V4L2_FIELD_TOP;
9500aa77f6cSMauro Carvalho Chehab 			} else if (!((field == V4L2_FIELD_TOP) ||
9510aa77f6cSMauro Carvalho Chehab 				     (field == V4L2_FIELD_BOTTOM))) {
9520aa77f6cSMauro Carvalho Chehab 				dprintk(1, "unsupported field setting\n");
9530aa77f6cSMauro Carvalho Chehab 				return -EINVAL;
9540aa77f6cSMauro Carvalho Chehab 			}
9550aa77f6cSMauro Carvalho Chehab 		}
9560aa77f6cSMauro Carvalho Chehab 		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) {
9570aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
9580aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_SEQ_TB;
9590aa77f6cSMauro Carvalho Chehab 		} else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) {
9600aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
9610aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
9620aa77f6cSMauro Carvalho Chehab 		} else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) {
9630aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
9640aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
9650aa77f6cSMauro Carvalho Chehab 		} else {
9660aa77f6cSMauro Carvalho Chehab 			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
9670aa77f6cSMauro Carvalho Chehab 			field = V4L2_FIELD_TOP;
9680aa77f6cSMauro Carvalho Chehab 		}
9690aa77f6cSMauro Carvalho Chehab 	}
9700aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.field = field;
9710aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
9720aa77f6cSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
9730aa77f6cSMauro Carvalho Chehab 	dprintk(50, "%s: set width %d height %d field %d\n", __func__,
9740aa77f6cSMauro Carvalho Chehab 		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
9750aa77f6cSMauro Carvalho Chehab 	return 0;
9760aa77f6cSMauro Carvalho Chehab }
9770aa77f6cSMauro Carvalho Chehab 
9780aa77f6cSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
9790aa77f6cSMauro Carvalho Chehab 			    struct v4l2_format *f)
9800aa77f6cSMauro Carvalho Chehab {
9810aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
9820aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
9830aa77f6cSMauro Carvalho Chehab 	const struct s2255_fmt *fmt;
9840aa77f6cSMauro Carvalho Chehab 	struct videobuf_queue *q = &fh->vb_vidq;
9850aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
9860aa77f6cSMauro Carvalho Chehab 	int ret;
9870aa77f6cSMauro Carvalho Chehab 
9880aa77f6cSMauro Carvalho Chehab 	ret = vidioc_try_fmt_vid_cap(file, fh, f);
9890aa77f6cSMauro Carvalho Chehab 
9900aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
9910aa77f6cSMauro Carvalho Chehab 		return ret;
9920aa77f6cSMauro Carvalho Chehab 
9930aa77f6cSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
9940aa77f6cSMauro Carvalho Chehab 
9950aa77f6cSMauro Carvalho Chehab 	if (fmt == NULL)
9960aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
9970aa77f6cSMauro Carvalho Chehab 
9980aa77f6cSMauro Carvalho Chehab 	mutex_lock(&q->vb_lock);
9990aa77f6cSMauro Carvalho Chehab 
10000aa77f6cSMauro Carvalho Chehab 	if (videobuf_queue_is_busy(&fh->vb_vidq)) {
10010aa77f6cSMauro Carvalho Chehab 		dprintk(1, "queue busy\n");
10020aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
10030aa77f6cSMauro Carvalho Chehab 		goto out_s_fmt;
10040aa77f6cSMauro Carvalho Chehab 	}
10050aa77f6cSMauro Carvalho Chehab 
10060aa77f6cSMauro Carvalho Chehab 	if (res_locked(fh)) {
10070aa77f6cSMauro Carvalho Chehab 		dprintk(1, "%s: channel busy\n", __func__);
10080aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
10090aa77f6cSMauro Carvalho Chehab 		goto out_s_fmt;
10100aa77f6cSMauro Carvalho Chehab 	}
10110aa77f6cSMauro Carvalho Chehab 	mode = channel->mode;
10120aa77f6cSMauro Carvalho Chehab 	channel->fmt = fmt;
10130aa77f6cSMauro Carvalho Chehab 	channel->width = f->fmt.pix.width;
10140aa77f6cSMauro Carvalho Chehab 	channel->height = f->fmt.pix.height;
10150aa77f6cSMauro Carvalho Chehab 	fh->vb_vidq.field = f->fmt.pix.field;
10160aa77f6cSMauro Carvalho Chehab 	fh->type = f->type;
10170aa77f6cSMauro Carvalho Chehab 	if (channel->width > norm_minw(&channel->vdev)) {
10180aa77f6cSMauro Carvalho Chehab 		if (channel->height > norm_minh(&channel->vdev)) {
10190aa77f6cSMauro Carvalho Chehab 			if (channel->cap_parm.capturemode &
10200aa77f6cSMauro Carvalho Chehab 			    V4L2_MODE_HIGHQUALITY)
10210aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFSI;
10220aa77f6cSMauro Carvalho Chehab 			else
10230aa77f6cSMauro Carvalho Chehab 				mode.scale = SCALE_4CIFS;
10240aa77f6cSMauro Carvalho Chehab 		} else
10250aa77f6cSMauro Carvalho Chehab 			mode.scale = SCALE_2CIFS;
10260aa77f6cSMauro Carvalho Chehab 
10270aa77f6cSMauro Carvalho Chehab 	} else {
10280aa77f6cSMauro Carvalho Chehab 		mode.scale = SCALE_1CIFS;
10290aa77f6cSMauro Carvalho Chehab 	}
10300aa77f6cSMauro Carvalho Chehab 	/* color mode */
10310aa77f6cSMauro Carvalho Chehab 	switch (channel->fmt->fourcc) {
10320aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_GREY:
10330aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
10340aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_Y8;
10350aa77f6cSMauro Carvalho Chehab 		break;
10360aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_JPEG:
10370aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_MJPEG:
10380aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
10390aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_JPG;
10400aa77f6cSMauro Carvalho Chehab 		mode.color |= (channel->jc.quality << 8);
10410aa77f6cSMauro Carvalho Chehab 		break;
10420aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUV422P:
10430aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
10440aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPL;
10450aa77f6cSMauro Carvalho Chehab 		break;
10460aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_YUYV:
10470aa77f6cSMauro Carvalho Chehab 	case V4L2_PIX_FMT_UYVY:
10480aa77f6cSMauro Carvalho Chehab 	default:
10490aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_COLOR;
10500aa77f6cSMauro Carvalho Chehab 		mode.color |= COLOR_YUVPK;
10510aa77f6cSMauro Carvalho Chehab 		break;
10520aa77f6cSMauro Carvalho Chehab 	}
10530aa77f6cSMauro Carvalho Chehab 	if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR))
10540aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
10550aa77f6cSMauro Carvalho Chehab 	else if (mode.scale != channel->mode.scale)
10560aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
10570aa77f6cSMauro Carvalho Chehab 	else if (mode.format != channel->mode.format)
10580aa77f6cSMauro Carvalho Chehab 		mode.restart = 1;
10590aa77f6cSMauro Carvalho Chehab 	channel->mode = mode;
10600aa77f6cSMauro Carvalho Chehab 	(void) s2255_set_mode(channel, &mode);
10610aa77f6cSMauro Carvalho Chehab 	ret = 0;
10620aa77f6cSMauro Carvalho Chehab out_s_fmt:
10630aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&q->vb_lock);
10640aa77f6cSMauro Carvalho Chehab 	return ret;
10650aa77f6cSMauro Carvalho Chehab }
10660aa77f6cSMauro Carvalho Chehab 
10670aa77f6cSMauro Carvalho Chehab static int vidioc_reqbufs(struct file *file, void *priv,
10680aa77f6cSMauro Carvalho Chehab 			  struct v4l2_requestbuffers *p)
10690aa77f6cSMauro Carvalho Chehab {
10700aa77f6cSMauro Carvalho Chehab 	int rc;
10710aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10720aa77f6cSMauro Carvalho Chehab 	rc = videobuf_reqbufs(&fh->vb_vidq, p);
10730aa77f6cSMauro Carvalho Chehab 	return rc;
10740aa77f6cSMauro Carvalho Chehab }
10750aa77f6cSMauro Carvalho Chehab 
10760aa77f6cSMauro Carvalho Chehab static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
10770aa77f6cSMauro Carvalho Chehab {
10780aa77f6cSMauro Carvalho Chehab 	int rc;
10790aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10800aa77f6cSMauro Carvalho Chehab 	rc = videobuf_querybuf(&fh->vb_vidq, p);
10810aa77f6cSMauro Carvalho Chehab 	return rc;
10820aa77f6cSMauro Carvalho Chehab }
10830aa77f6cSMauro Carvalho Chehab 
10840aa77f6cSMauro Carvalho Chehab static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
10850aa77f6cSMauro Carvalho Chehab {
10860aa77f6cSMauro Carvalho Chehab 	int rc;
10870aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10880aa77f6cSMauro Carvalho Chehab 	rc = videobuf_qbuf(&fh->vb_vidq, p);
10890aa77f6cSMauro Carvalho Chehab 	return rc;
10900aa77f6cSMauro Carvalho Chehab }
10910aa77f6cSMauro Carvalho Chehab 
10920aa77f6cSMauro Carvalho Chehab static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
10930aa77f6cSMauro Carvalho Chehab {
10940aa77f6cSMauro Carvalho Chehab 	int rc;
10950aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
10960aa77f6cSMauro Carvalho Chehab 	rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
10970aa77f6cSMauro Carvalho Chehab 	return rc;
10980aa77f6cSMauro Carvalho Chehab }
10990aa77f6cSMauro Carvalho Chehab 
11000aa77f6cSMauro Carvalho Chehab /* write to the configuration pipe, synchronously */
11010aa77f6cSMauro Carvalho Chehab static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
11020aa77f6cSMauro Carvalho Chehab 			      int size)
11030aa77f6cSMauro Carvalho Chehab {
11040aa77f6cSMauro Carvalho Chehab 	int pipe;
11050aa77f6cSMauro Carvalho Chehab 	int done;
11060aa77f6cSMauro Carvalho Chehab 	long retval = -1;
11070aa77f6cSMauro Carvalho Chehab 	if (udev) {
11080aa77f6cSMauro Carvalho Chehab 		pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
11090aa77f6cSMauro Carvalho Chehab 		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
11100aa77f6cSMauro Carvalho Chehab 	}
11110aa77f6cSMauro Carvalho Chehab 	return retval;
11120aa77f6cSMauro Carvalho Chehab }
11130aa77f6cSMauro Carvalho Chehab 
11140aa77f6cSMauro Carvalho Chehab static u32 get_transfer_size(struct s2255_mode *mode)
11150aa77f6cSMauro Carvalho Chehab {
11160aa77f6cSMauro Carvalho Chehab 	int linesPerFrame = LINE_SZ_DEF;
11170aa77f6cSMauro Carvalho Chehab 	int pixelsPerLine = NUM_LINES_DEF;
11180aa77f6cSMauro Carvalho Chehab 	u32 outImageSize;
11190aa77f6cSMauro Carvalho Chehab 	u32 usbInSize;
11200aa77f6cSMauro Carvalho Chehab 	unsigned int mask_mult;
11210aa77f6cSMauro Carvalho Chehab 
11220aa77f6cSMauro Carvalho Chehab 	if (mode == NULL)
11230aa77f6cSMauro Carvalho Chehab 		return 0;
11240aa77f6cSMauro Carvalho Chehab 
11250aa77f6cSMauro Carvalho Chehab 	if (mode->format == FORMAT_NTSC) {
11260aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
11270aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
11280aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
11290aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
11300aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
11310aa77f6cSMauro Carvalho Chehab 			break;
11320aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
11330aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_NTSC;
11340aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
11350aa77f6cSMauro Carvalho Chehab 			break;
11360aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
11370aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_NTSC;
11380aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
11390aa77f6cSMauro Carvalho Chehab 			break;
11400aa77f6cSMauro Carvalho Chehab 		default:
11410aa77f6cSMauro Carvalho Chehab 			break;
11420aa77f6cSMauro Carvalho Chehab 		}
11430aa77f6cSMauro Carvalho Chehab 	} else if (mode->format == FORMAT_PAL) {
11440aa77f6cSMauro Carvalho Chehab 		switch (mode->scale) {
11450aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFS:
11460aa77f6cSMauro Carvalho Chehab 		case SCALE_4CIFSI:
11470aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
11480aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_4CIFS_PAL;
11490aa77f6cSMauro Carvalho Chehab 			break;
11500aa77f6cSMauro Carvalho Chehab 		case SCALE_2CIFS:
11510aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_2CIFS_PAL;
11520aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_2CIFS_PAL;
11530aa77f6cSMauro Carvalho Chehab 			break;
11540aa77f6cSMauro Carvalho Chehab 		case SCALE_1CIFS:
11550aa77f6cSMauro Carvalho Chehab 			linesPerFrame = NUM_LINES_1CIFS_PAL;
11560aa77f6cSMauro Carvalho Chehab 			pixelsPerLine = LINE_SZ_1CIFS_PAL;
11570aa77f6cSMauro Carvalho Chehab 			break;
11580aa77f6cSMauro Carvalho Chehab 		default:
11590aa77f6cSMauro Carvalho Chehab 			break;
11600aa77f6cSMauro Carvalho Chehab 		}
11610aa77f6cSMauro Carvalho Chehab 	}
11620aa77f6cSMauro Carvalho Chehab 	outImageSize = linesPerFrame * pixelsPerLine;
11630aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) != COLOR_Y8) {
11640aa77f6cSMauro Carvalho Chehab 		/* 2 bytes/pixel if not monochrome */
11650aa77f6cSMauro Carvalho Chehab 		outImageSize *= 2;
11660aa77f6cSMauro Carvalho Chehab 	}
11670aa77f6cSMauro Carvalho Chehab 
11680aa77f6cSMauro Carvalho Chehab 	/* total bytes to send including prefix and 4K padding;
11690aa77f6cSMauro Carvalho Chehab 	   must be a multiple of USB_READ_SIZE */
11700aa77f6cSMauro Carvalho Chehab 	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
11710aa77f6cSMauro Carvalho Chehab 	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
11720aa77f6cSMauro Carvalho Chehab 	/* if size not a multiple of USB_READ_SIZE */
11730aa77f6cSMauro Carvalho Chehab 	if (usbInSize & ~mask_mult)
11740aa77f6cSMauro Carvalho Chehab 		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
11750aa77f6cSMauro Carvalho Chehab 	return usbInSize;
11760aa77f6cSMauro Carvalho Chehab }
11770aa77f6cSMauro Carvalho Chehab 
11780aa77f6cSMauro Carvalho Chehab static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
11790aa77f6cSMauro Carvalho Chehab {
11800aa77f6cSMauro Carvalho Chehab 	struct device *dev = &sdev->udev->dev;
11810aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
11820aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
11830aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
11840aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "bright: 0x%x\n", mode->bright);
11850aa77f6cSMauro Carvalho Chehab 	dev_info(dev, "------------------------------------------------\n");
11860aa77f6cSMauro Carvalho Chehab }
11870aa77f6cSMauro Carvalho Chehab 
11880aa77f6cSMauro Carvalho Chehab /*
11890aa77f6cSMauro Carvalho Chehab  * set mode is the function which controls the DSP.
11900aa77f6cSMauro Carvalho Chehab  * the restart parameter in struct s2255_mode should be set whenever
11910aa77f6cSMauro Carvalho Chehab  * the image size could change via color format, video system or image
11920aa77f6cSMauro Carvalho Chehab  * size.
11930aa77f6cSMauro Carvalho Chehab  * When the restart parameter is set, we sleep for ONE frame to allow the
11940aa77f6cSMauro Carvalho Chehab  * DSP time to get the new frame
11950aa77f6cSMauro Carvalho Chehab  */
11960aa77f6cSMauro Carvalho Chehab static int s2255_set_mode(struct s2255_channel *channel,
11970aa77f6cSMauro Carvalho Chehab 			  struct s2255_mode *mode)
11980aa77f6cSMauro Carvalho Chehab {
11990aa77f6cSMauro Carvalho Chehab 	int res;
12000aa77f6cSMauro Carvalho Chehab 	__le32 *buffer;
12010aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
12020aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
12030aa77f6cSMauro Carvalho Chehab 	chn_rev = G_chnmap[channel->idx];
12040aa77f6cSMauro Carvalho Chehab 	dprintk(3, "%s channel: %d\n", __func__, channel->idx);
12050aa77f6cSMauro Carvalho Chehab 	/* if JPEG, set the quality */
12060aa77f6cSMauro Carvalho Chehab 	if ((mode->color & MASK_COLOR) == COLOR_JPG) {
12070aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_COLOR;
12080aa77f6cSMauro Carvalho Chehab 		mode->color |= COLOR_JPG;
12090aa77f6cSMauro Carvalho Chehab 		mode->color &= ~MASK_JPG_QUALITY;
12100aa77f6cSMauro Carvalho Chehab 		mode->color |= (channel->jc.quality << 8);
12110aa77f6cSMauro Carvalho Chehab 	}
12120aa77f6cSMauro Carvalho Chehab 	/* save the mode */
12130aa77f6cSMauro Carvalho Chehab 	channel->mode = *mode;
12140aa77f6cSMauro Carvalho Chehab 	channel->req_image_size = get_transfer_size(mode);
12150aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size);
12160aa77f6cSMauro Carvalho Chehab 	buffer = kzalloc(512, GFP_KERNEL);
12170aa77f6cSMauro Carvalho Chehab 	if (buffer == NULL) {
12180aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "out of mem\n");
12190aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
12200aa77f6cSMauro Carvalho Chehab 	}
12210aa77f6cSMauro Carvalho Chehab 	/* set the mode */
12220aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
12230aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
12240aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_SET_MODE;
12250aa77f6cSMauro Carvalho Chehab 	memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode));
12260aa77f6cSMauro Carvalho Chehab 	channel->setmode_ready = 0;
12270aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
12280aa77f6cSMauro Carvalho Chehab 	if (debug)
12290aa77f6cSMauro Carvalho Chehab 		s2255_print_cfg(dev, mode);
12300aa77f6cSMauro Carvalho Chehab 	kfree(buffer);
12310aa77f6cSMauro Carvalho Chehab 	/* wait at least 3 frames before continuing */
12320aa77f6cSMauro Carvalho Chehab 	if (mode->restart) {
12330aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(channel->wait_setmode,
12340aa77f6cSMauro Carvalho Chehab 				   (channel->setmode_ready != 0),
12350aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
12360aa77f6cSMauro Carvalho Chehab 		if (channel->setmode_ready != 1) {
12370aa77f6cSMauro Carvalho Chehab 			printk(KERN_DEBUG "s2255: no set mode response\n");
12380aa77f6cSMauro Carvalho Chehab 			res = -EFAULT;
12390aa77f6cSMauro Carvalho Chehab 		}
12400aa77f6cSMauro Carvalho Chehab 	}
12410aa77f6cSMauro Carvalho Chehab 	/* clear the restart flag */
12420aa77f6cSMauro Carvalho Chehab 	channel->mode.restart = 0;
12430aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res);
12440aa77f6cSMauro Carvalho Chehab 	return res;
12450aa77f6cSMauro Carvalho Chehab }
12460aa77f6cSMauro Carvalho Chehab 
12470aa77f6cSMauro Carvalho Chehab static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus)
12480aa77f6cSMauro Carvalho Chehab {
12490aa77f6cSMauro Carvalho Chehab 	int res;
12500aa77f6cSMauro Carvalho Chehab 	__le32 *buffer;
12510aa77f6cSMauro Carvalho Chehab 	u32 chn_rev;
12520aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
12530aa77f6cSMauro Carvalho Chehab 	chn_rev = G_chnmap[channel->idx];
12540aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s chan %d\n", __func__, channel->idx);
12550aa77f6cSMauro Carvalho Chehab 	buffer = kzalloc(512, GFP_KERNEL);
12560aa77f6cSMauro Carvalho Chehab 	if (buffer == NULL) {
12570aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "out of mem\n");
12580aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
12590aa77f6cSMauro Carvalho Chehab 	}
12600aa77f6cSMauro Carvalho Chehab 	/* form the get vid status command */
12610aa77f6cSMauro Carvalho Chehab 	buffer[0] = IN_DATA_TOKEN;
12620aa77f6cSMauro Carvalho Chehab 	buffer[1] = (__le32) cpu_to_le32(chn_rev);
12630aa77f6cSMauro Carvalho Chehab 	buffer[2] = CMD_STATUS;
12640aa77f6cSMauro Carvalho Chehab 	*pstatus = 0;
12650aa77f6cSMauro Carvalho Chehab 	channel->vidstatus_ready = 0;
12660aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
12670aa77f6cSMauro Carvalho Chehab 	kfree(buffer);
12680aa77f6cSMauro Carvalho Chehab 	wait_event_timeout(channel->wait_vidstatus,
12690aa77f6cSMauro Carvalho Chehab 			   (channel->vidstatus_ready != 0),
12700aa77f6cSMauro Carvalho Chehab 			   msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
12710aa77f6cSMauro Carvalho Chehab 	if (channel->vidstatus_ready != 1) {
12720aa77f6cSMauro Carvalho Chehab 		printk(KERN_DEBUG "s2255: no vidstatus response\n");
12730aa77f6cSMauro Carvalho Chehab 		res = -EFAULT;
12740aa77f6cSMauro Carvalho Chehab 	}
12750aa77f6cSMauro Carvalho Chehab 	*pstatus = channel->vidstatus;
12760aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s, vid status %d\n", __func__, *pstatus);
12770aa77f6cSMauro Carvalho Chehab 	return res;
12780aa77f6cSMauro Carvalho Chehab }
12790aa77f6cSMauro Carvalho Chehab 
12800aa77f6cSMauro Carvalho Chehab static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
12810aa77f6cSMauro Carvalho Chehab {
12820aa77f6cSMauro Carvalho Chehab 	int res;
12830aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
12840aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
12850aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
12860aa77f6cSMauro Carvalho Chehab 	int j;
12870aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s\n", __func__);
12880aa77f6cSMauro Carvalho Chehab 	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
12890aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid fh type0\n");
12900aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12910aa77f6cSMauro Carvalho Chehab 	}
12920aa77f6cSMauro Carvalho Chehab 	if (i != fh->type) {
12930aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid fh type1\n");
12940aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
12950aa77f6cSMauro Carvalho Chehab 	}
12960aa77f6cSMauro Carvalho Chehab 
12970aa77f6cSMauro Carvalho Chehab 	if (!res_get(fh)) {
12980aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&dev->udev->dev, "stream busy\n");
12990aa77f6cSMauro Carvalho Chehab 		return -EBUSY;
13000aa77f6cSMauro Carvalho Chehab 	}
13010aa77f6cSMauro Carvalho Chehab 	channel->last_frame = -1;
13020aa77f6cSMauro Carvalho Chehab 	channel->bad_payload = 0;
13030aa77f6cSMauro Carvalho Chehab 	channel->cur_frame = 0;
13040aa77f6cSMauro Carvalho Chehab 	channel->frame_count = 0;
13050aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
13060aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[j].ulState = S2255_READ_IDLE;
13070aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[j].cur_size = 0;
13080aa77f6cSMauro Carvalho Chehab 	}
13090aa77f6cSMauro Carvalho Chehab 	res = videobuf_streamon(&fh->vb_vidq);
13100aa77f6cSMauro Carvalho Chehab 	if (res == 0) {
13110aa77f6cSMauro Carvalho Chehab 		s2255_start_acquire(channel);
13120aa77f6cSMauro Carvalho Chehab 		channel->b_acquire = 1;
13130aa77f6cSMauro Carvalho Chehab 	} else
13140aa77f6cSMauro Carvalho Chehab 		res_free(fh);
13150aa77f6cSMauro Carvalho Chehab 
13160aa77f6cSMauro Carvalho Chehab 	return res;
13170aa77f6cSMauro Carvalho Chehab }
13180aa77f6cSMauro Carvalho Chehab 
13190aa77f6cSMauro Carvalho Chehab static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
13200aa77f6cSMauro Carvalho Chehab {
13210aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
13220aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx);
13230aa77f6cSMauro Carvalho Chehab 	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
13240aa77f6cSMauro Carvalho Chehab 		printk(KERN_ERR "invalid fh type0\n");
13250aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13260aa77f6cSMauro Carvalho Chehab 	}
13270aa77f6cSMauro Carvalho Chehab 	if (i != fh->type) {
13280aa77f6cSMauro Carvalho Chehab 		printk(KERN_ERR "invalid type i\n");
13290aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13300aa77f6cSMauro Carvalho Chehab 	}
13310aa77f6cSMauro Carvalho Chehab 	s2255_stop_acquire(fh->channel);
13320aa77f6cSMauro Carvalho Chehab 	videobuf_streamoff(&fh->vb_vidq);
13330aa77f6cSMauro Carvalho Chehab 	res_free(fh);
13340aa77f6cSMauro Carvalho Chehab 	return 0;
13350aa77f6cSMauro Carvalho Chehab }
13360aa77f6cSMauro Carvalho Chehab 
13370aa77f6cSMauro Carvalho Chehab static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
13380aa77f6cSMauro Carvalho Chehab {
13390aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
13400aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
13410aa77f6cSMauro Carvalho Chehab 	struct videobuf_queue *q = &fh->vb_vidq;
13420aa77f6cSMauro Carvalho Chehab 	int ret = 0;
13430aa77f6cSMauro Carvalho Chehab 	mutex_lock(&q->vb_lock);
13440aa77f6cSMauro Carvalho Chehab 	if (videobuf_queue_is_busy(q)) {
13450aa77f6cSMauro Carvalho Chehab 		dprintk(1, "queue busy\n");
13460aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
13470aa77f6cSMauro Carvalho Chehab 		goto out_s_std;
13480aa77f6cSMauro Carvalho Chehab 	}
13490aa77f6cSMauro Carvalho Chehab 	if (res_locked(fh)) {
13500aa77f6cSMauro Carvalho Chehab 		dprintk(1, "can't change standard after started\n");
13510aa77f6cSMauro Carvalho Chehab 		ret = -EBUSY;
13520aa77f6cSMauro Carvalho Chehab 		goto out_s_std;
13530aa77f6cSMauro Carvalho Chehab 	}
13540aa77f6cSMauro Carvalho Chehab 	mode = fh->channel->mode;
13550aa77f6cSMauro Carvalho Chehab 	if (*i & V4L2_STD_NTSC) {
13560aa77f6cSMauro Carvalho Chehab 		dprintk(4, "%s NTSC\n", __func__);
13570aa77f6cSMauro Carvalho Chehab 		/* if changing format, reset frame decimation/intervals */
13580aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_NTSC) {
13590aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
13600aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_NTSC;
13610aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
13620aa77f6cSMauro Carvalho Chehab 		}
13630aa77f6cSMauro Carvalho Chehab 	} else if (*i & V4L2_STD_PAL) {
13640aa77f6cSMauro Carvalho Chehab 		dprintk(4, "%s PAL\n", __func__);
13650aa77f6cSMauro Carvalho Chehab 		if (mode.format != FORMAT_PAL) {
13660aa77f6cSMauro Carvalho Chehab 			mode.restart = 1;
13670aa77f6cSMauro Carvalho Chehab 			mode.format = FORMAT_PAL;
13680aa77f6cSMauro Carvalho Chehab 			mode.fdec = FDEC_1;
13690aa77f6cSMauro Carvalho Chehab 		}
13700aa77f6cSMauro Carvalho Chehab 	} else {
13710aa77f6cSMauro Carvalho Chehab 		ret = -EINVAL;
13720aa77f6cSMauro Carvalho Chehab 	}
13730aa77f6cSMauro Carvalho Chehab 	if (mode.restart)
13740aa77f6cSMauro Carvalho Chehab 		s2255_set_mode(fh->channel, &mode);
13750aa77f6cSMauro Carvalho Chehab out_s_std:
13760aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&q->vb_lock);
13770aa77f6cSMauro Carvalho Chehab 	return ret;
13780aa77f6cSMauro Carvalho Chehab }
13790aa77f6cSMauro Carvalho Chehab 
13800aa77f6cSMauro Carvalho Chehab /* Sensoray 2255 is a multiple channel capture device.
13810aa77f6cSMauro Carvalho Chehab    It does not have a "crossbar" of inputs.
13820aa77f6cSMauro Carvalho Chehab    We use one V4L device per channel. The user must
13830aa77f6cSMauro Carvalho Chehab    be aware that certain combinations are not allowed.
13840aa77f6cSMauro Carvalho Chehab    For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
13850aa77f6cSMauro Carvalho Chehab    at once in color(you can do full fps on 4 channels with greyscale.
13860aa77f6cSMauro Carvalho Chehab */
13870aa77f6cSMauro Carvalho Chehab static int vidioc_enum_input(struct file *file, void *priv,
13880aa77f6cSMauro Carvalho Chehab 			     struct v4l2_input *inp)
13890aa77f6cSMauro Carvalho Chehab {
13900aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
13910aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
13920aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
13930aa77f6cSMauro Carvalho Chehab 	u32 status = 0;
13940aa77f6cSMauro Carvalho Chehab 	if (inp->index != 0)
13950aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
13960aa77f6cSMauro Carvalho Chehab 	inp->type = V4L2_INPUT_TYPE_CAMERA;
13970aa77f6cSMauro Carvalho Chehab 	inp->std = S2255_NORMS;
13980aa77f6cSMauro Carvalho Chehab 	inp->status = 0;
13990aa77f6cSMauro Carvalho Chehab 	if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
14000aa77f6cSMauro Carvalho Chehab 		int rc;
14010aa77f6cSMauro Carvalho Chehab 		rc = s2255_cmd_status(fh->channel, &status);
14020aa77f6cSMauro Carvalho Chehab 		dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status);
14030aa77f6cSMauro Carvalho Chehab 		if (rc == 0)
14040aa77f6cSMauro Carvalho Chehab 			inp->status =  (status & 0x01) ? 0
14050aa77f6cSMauro Carvalho Chehab 				: V4L2_IN_ST_NO_SIGNAL;
14060aa77f6cSMauro Carvalho Chehab 	}
14070aa77f6cSMauro Carvalho Chehab 	switch (dev->pid) {
14080aa77f6cSMauro Carvalho Chehab 	case 0x2255:
14090aa77f6cSMauro Carvalho Chehab 	default:
14100aa77f6cSMauro Carvalho Chehab 		strlcpy(inp->name, "Composite", sizeof(inp->name));
14110aa77f6cSMauro Carvalho Chehab 		break;
14120aa77f6cSMauro Carvalho Chehab 	case 0x2257:
14130aa77f6cSMauro Carvalho Chehab 		strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video",
14140aa77f6cSMauro Carvalho Chehab 			sizeof(inp->name));
14150aa77f6cSMauro Carvalho Chehab 		break;
14160aa77f6cSMauro Carvalho Chehab 	}
14170aa77f6cSMauro Carvalho Chehab 	return 0;
14180aa77f6cSMauro Carvalho Chehab }
14190aa77f6cSMauro Carvalho Chehab 
14200aa77f6cSMauro Carvalho Chehab static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
14210aa77f6cSMauro Carvalho Chehab {
14220aa77f6cSMauro Carvalho Chehab 	*i = 0;
14230aa77f6cSMauro Carvalho Chehab 	return 0;
14240aa77f6cSMauro Carvalho Chehab }
14250aa77f6cSMauro Carvalho Chehab static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
14260aa77f6cSMauro Carvalho Chehab {
14270aa77f6cSMauro Carvalho Chehab 	if (i > 0)
14280aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
14290aa77f6cSMauro Carvalho Chehab 	return 0;
14300aa77f6cSMauro Carvalho Chehab }
14310aa77f6cSMauro Carvalho Chehab 
14320aa77f6cSMauro Carvalho Chehab /* --- controls ---------------------------------------------- */
14330aa77f6cSMauro Carvalho Chehab static int vidioc_queryctrl(struct file *file, void *priv,
14340aa77f6cSMauro Carvalho Chehab 			    struct v4l2_queryctrl *qc)
14350aa77f6cSMauro Carvalho Chehab {
14360aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
14370aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
14380aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
14390aa77f6cSMauro Carvalho Chehab 	switch (qc->id) {
14400aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
14410aa77f6cSMauro Carvalho Chehab 		v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT);
14420aa77f6cSMauro Carvalho Chehab 		break;
14430aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
14440aa77f6cSMauro Carvalho Chehab 		v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST);
14450aa77f6cSMauro Carvalho Chehab 		break;
14460aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
14470aa77f6cSMauro Carvalho Chehab 		v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION);
14480aa77f6cSMauro Carvalho Chehab 		break;
14490aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_HUE:
14500aa77f6cSMauro Carvalho Chehab 		v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE);
14510aa77f6cSMauro Carvalho Chehab 		break;
14520aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_PRIVATE_COLORFILTER:
14530aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
14540aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
14550aa77f6cSMauro Carvalho Chehab 		if ((dev->pid == 0x2257) && (channel->idx > 1))
14560aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
14570aa77f6cSMauro Carvalho Chehab 		strlcpy(qc->name, "Color Filter", sizeof(qc->name));
14580aa77f6cSMauro Carvalho Chehab 		qc->type = V4L2_CTRL_TYPE_MENU;
14590aa77f6cSMauro Carvalho Chehab 		qc->minimum = 0;
14600aa77f6cSMauro Carvalho Chehab 		qc->maximum = 1;
14610aa77f6cSMauro Carvalho Chehab 		qc->step = 1;
14620aa77f6cSMauro Carvalho Chehab 		qc->default_value = 1;
14630aa77f6cSMauro Carvalho Chehab 		qc->flags = 0;
14640aa77f6cSMauro Carvalho Chehab 		break;
14650aa77f6cSMauro Carvalho Chehab 	default:
14660aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
14670aa77f6cSMauro Carvalho Chehab 	}
14680aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s, id %d\n", __func__, qc->id);
14690aa77f6cSMauro Carvalho Chehab 	return 0;
14700aa77f6cSMauro Carvalho Chehab }
14710aa77f6cSMauro Carvalho Chehab 
14720aa77f6cSMauro Carvalho Chehab static int vidioc_g_ctrl(struct file *file, void *priv,
14730aa77f6cSMauro Carvalho Chehab 			 struct v4l2_control *ctrl)
14740aa77f6cSMauro Carvalho Chehab {
14750aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
14760aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
14770aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
14780aa77f6cSMauro Carvalho Chehab 	switch (ctrl->id) {
14790aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
14800aa77f6cSMauro Carvalho Chehab 		ctrl->value = channel->mode.bright;
14810aa77f6cSMauro Carvalho Chehab 		break;
14820aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
14830aa77f6cSMauro Carvalho Chehab 		ctrl->value = channel->mode.contrast;
14840aa77f6cSMauro Carvalho Chehab 		break;
14850aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
14860aa77f6cSMauro Carvalho Chehab 		ctrl->value = channel->mode.saturation;
14870aa77f6cSMauro Carvalho Chehab 		break;
14880aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_HUE:
14890aa77f6cSMauro Carvalho Chehab 		ctrl->value = channel->mode.hue;
14900aa77f6cSMauro Carvalho Chehab 		break;
14910aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_PRIVATE_COLORFILTER:
14920aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
14930aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
14940aa77f6cSMauro Carvalho Chehab 		if ((dev->pid == 0x2257) && (channel->idx > 1))
14950aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
14960aa77f6cSMauro Carvalho Chehab 		ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16);
14970aa77f6cSMauro Carvalho Chehab 		break;
14980aa77f6cSMauro Carvalho Chehab 	default:
14990aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
15000aa77f6cSMauro Carvalho Chehab 	}
15010aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value);
15020aa77f6cSMauro Carvalho Chehab 	return 0;
15030aa77f6cSMauro Carvalho Chehab }
15040aa77f6cSMauro Carvalho Chehab 
15050aa77f6cSMauro Carvalho Chehab static int vidioc_s_ctrl(struct file *file, void *priv,
15060aa77f6cSMauro Carvalho Chehab 			 struct v4l2_control *ctrl)
15070aa77f6cSMauro Carvalho Chehab {
15080aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
15090aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
15100aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
15110aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
15120aa77f6cSMauro Carvalho Chehab 	mode = channel->mode;
15130aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s\n", __func__);
15140aa77f6cSMauro Carvalho Chehab 	/* update the mode to the corresponding value */
15150aa77f6cSMauro Carvalho Chehab 	switch (ctrl->id) {
15160aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
15170aa77f6cSMauro Carvalho Chehab 		mode.bright = ctrl->value;
15180aa77f6cSMauro Carvalho Chehab 		break;
15190aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
15200aa77f6cSMauro Carvalho Chehab 		mode.contrast = ctrl->value;
15210aa77f6cSMauro Carvalho Chehab 		break;
15220aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_HUE:
15230aa77f6cSMauro Carvalho Chehab 		mode.hue = ctrl->value;
15240aa77f6cSMauro Carvalho Chehab 		break;
15250aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
15260aa77f6cSMauro Carvalho Chehab 		mode.saturation = ctrl->value;
15270aa77f6cSMauro Carvalho Chehab 		break;
15280aa77f6cSMauro Carvalho Chehab 	case V4L2_CID_PRIVATE_COLORFILTER:
15290aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
15300aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
15310aa77f6cSMauro Carvalho Chehab 		if ((dev->pid == 0x2257) && (channel->idx > 1))
15320aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
15330aa77f6cSMauro Carvalho Chehab 		mode.color &= ~MASK_INPUT_TYPE;
15340aa77f6cSMauro Carvalho Chehab 		mode.color |= ((ctrl->value ? 0 : 1) << 16);
15350aa77f6cSMauro Carvalho Chehab 		break;
15360aa77f6cSMauro Carvalho Chehab 	default:
15370aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
15380aa77f6cSMauro Carvalho Chehab 	}
15390aa77f6cSMauro Carvalho Chehab 	mode.restart = 0;
15400aa77f6cSMauro Carvalho Chehab 	/* set mode here.  Note: stream does not need restarted.
15410aa77f6cSMauro Carvalho Chehab 	   some V4L programs restart stream unnecessarily
15420aa77f6cSMauro Carvalho Chehab 	   after a s_crtl.
15430aa77f6cSMauro Carvalho Chehab 	*/
15440aa77f6cSMauro Carvalho Chehab 	s2255_set_mode(fh->channel, &mode);
15450aa77f6cSMauro Carvalho Chehab 	return 0;
15460aa77f6cSMauro Carvalho Chehab }
15470aa77f6cSMauro Carvalho Chehab 
15480aa77f6cSMauro Carvalho Chehab static int vidioc_g_jpegcomp(struct file *file, void *priv,
15490aa77f6cSMauro Carvalho Chehab 			 struct v4l2_jpegcompression *jc)
15500aa77f6cSMauro Carvalho Chehab {
15510aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
15520aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
15530aa77f6cSMauro Carvalho Chehab 	*jc = channel->jc;
15540aa77f6cSMauro Carvalho Chehab 	dprintk(2, "%s: quality %d\n", __func__, jc->quality);
15550aa77f6cSMauro Carvalho Chehab 	return 0;
15560aa77f6cSMauro Carvalho Chehab }
15570aa77f6cSMauro Carvalho Chehab 
15580aa77f6cSMauro Carvalho Chehab static int vidioc_s_jpegcomp(struct file *file, void *priv,
1559*d88aab53SHans Verkuil 			 const struct v4l2_jpegcompression *jc)
15600aa77f6cSMauro Carvalho Chehab {
15610aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
15620aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
15630aa77f6cSMauro Carvalho Chehab 	if (jc->quality < 0 || jc->quality > 100)
15640aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
15650aa77f6cSMauro Carvalho Chehab 	channel->jc.quality = jc->quality;
15660aa77f6cSMauro Carvalho Chehab 	dprintk(2, "%s: quality %d\n", __func__, jc->quality);
15670aa77f6cSMauro Carvalho Chehab 	return 0;
15680aa77f6cSMauro Carvalho Chehab }
15690aa77f6cSMauro Carvalho Chehab 
15700aa77f6cSMauro Carvalho Chehab static int vidioc_g_parm(struct file *file, void *priv,
15710aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
15720aa77f6cSMauro Carvalho Chehab {
15730aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
15740aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
15750aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
15760aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
15770aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
15780aa77f6cSMauro Carvalho Chehab 	memset(sp, 0, sizeof(struct v4l2_streamparm));
15790aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
15800aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.capturemode = channel->cap_parm.capturemode;
15810aa77f6cSMauro Carvalho Chehab 	def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000;
15820aa77f6cSMauro Carvalho Chehab 	def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000;
15830aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
15840aa77f6cSMauro Carvalho Chehab 	switch (channel->mode.fdec) {
15850aa77f6cSMauro Carvalho Chehab 	default:
15860aa77f6cSMauro Carvalho Chehab 	case FDEC_1:
15870aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
15880aa77f6cSMauro Carvalho Chehab 		break;
15890aa77f6cSMauro Carvalho Chehab 	case FDEC_2:
15900aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
15910aa77f6cSMauro Carvalho Chehab 		break;
15920aa77f6cSMauro Carvalho Chehab 	case FDEC_3:
15930aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
15940aa77f6cSMauro Carvalho Chehab 		break;
15950aa77f6cSMauro Carvalho Chehab 	case FDEC_5:
15960aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
15970aa77f6cSMauro Carvalho Chehab 		break;
15980aa77f6cSMauro Carvalho Chehab 	}
15990aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__,
16000aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
16010aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
16020aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator);
16030aa77f6cSMauro Carvalho Chehab 	return 0;
16040aa77f6cSMauro Carvalho Chehab }
16050aa77f6cSMauro Carvalho Chehab 
16060aa77f6cSMauro Carvalho Chehab static int vidioc_s_parm(struct file *file, void *priv,
16070aa77f6cSMauro Carvalho Chehab 			 struct v4l2_streamparm *sp)
16080aa77f6cSMauro Carvalho Chehab {
16090aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = priv;
16100aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
16110aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode;
16120aa77f6cSMauro Carvalho Chehab 	int fdec = FDEC_1;
16130aa77f6cSMauro Carvalho Chehab 	__u32 def_num, def_dem;
16140aa77f6cSMauro Carvalho Chehab 	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
16150aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
16160aa77f6cSMauro Carvalho Chehab 	mode = channel->mode;
16170aa77f6cSMauro Carvalho Chehab 	/* high quality capture mode requires a stream restart */
16180aa77f6cSMauro Carvalho Chehab 	if (channel->cap_parm.capturemode
16190aa77f6cSMauro Carvalho Chehab 	    != sp->parm.capture.capturemode && res_locked(fh))
16200aa77f6cSMauro Carvalho Chehab 		return -EBUSY;
16210aa77f6cSMauro Carvalho Chehab 	def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
16220aa77f6cSMauro Carvalho Chehab 	def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
16230aa77f6cSMauro Carvalho Chehab 	if (def_dem != sp->parm.capture.timeperframe.denominator)
16240aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
16250aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= def_num)
16260aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num;
16270aa77f6cSMauro Carvalho Chehab 	else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
16280aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 2;
16290aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_2;
16300aa77f6cSMauro Carvalho Chehab 	} else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
16310aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 3;
16320aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_3;
16330aa77f6cSMauro Carvalho Chehab 	} else {
16340aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator = def_num * 5;
16350aa77f6cSMauro Carvalho Chehab 		fdec = FDEC_5;
16360aa77f6cSMauro Carvalho Chehab 	}
16370aa77f6cSMauro Carvalho Chehab 	mode.fdec = fdec;
16380aa77f6cSMauro Carvalho Chehab 	sp->parm.capture.timeperframe.denominator = def_dem;
16390aa77f6cSMauro Carvalho Chehab 	s2255_set_mode(channel, &mode);
16400aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
16410aa77f6cSMauro Carvalho Chehab 		__func__,
16420aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.capturemode,
16430aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.numerator,
16440aa77f6cSMauro Carvalho Chehab 		sp->parm.capture.timeperframe.denominator, fdec);
16450aa77f6cSMauro Carvalho Chehab 	return 0;
16460aa77f6cSMauro Carvalho Chehab }
16470aa77f6cSMauro Carvalho Chehab 
16480aa77f6cSMauro Carvalho Chehab static int vidioc_enum_frameintervals(struct file *file, void *priv,
16490aa77f6cSMauro Carvalho Chehab 			    struct v4l2_frmivalenum *fe)
16500aa77f6cSMauro Carvalho Chehab {
16510aa77f6cSMauro Carvalho Chehab 	int is_ntsc = 0;
16520aa77f6cSMauro Carvalho Chehab #define NUM_FRAME_ENUMS 4
16530aa77f6cSMauro Carvalho Chehab 	int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
16540aa77f6cSMauro Carvalho Chehab 	if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS)
16550aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
16560aa77f6cSMauro Carvalho Chehab 	switch (fe->width) {
16570aa77f6cSMauro Carvalho Chehab 	case 640:
16580aa77f6cSMauro Carvalho Chehab 		if (fe->height != 240 && fe->height != 480)
16590aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
16600aa77f6cSMauro Carvalho Chehab 		is_ntsc = 1;
16610aa77f6cSMauro Carvalho Chehab 		break;
16620aa77f6cSMauro Carvalho Chehab 	case 320:
16630aa77f6cSMauro Carvalho Chehab 		if (fe->height != 240)
16640aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
16650aa77f6cSMauro Carvalho Chehab 		is_ntsc = 1;
16660aa77f6cSMauro Carvalho Chehab 		break;
16670aa77f6cSMauro Carvalho Chehab 	case 704:
16680aa77f6cSMauro Carvalho Chehab 		if (fe->height != 288 && fe->height != 576)
16690aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
16700aa77f6cSMauro Carvalho Chehab 		break;
16710aa77f6cSMauro Carvalho Chehab 	case 352:
16720aa77f6cSMauro Carvalho Chehab 		if (fe->height != 288)
16730aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
16740aa77f6cSMauro Carvalho Chehab 		break;
16750aa77f6cSMauro Carvalho Chehab 	default:
16760aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
16770aa77f6cSMauro Carvalho Chehab 	}
16780aa77f6cSMauro Carvalho Chehab 	fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
16790aa77f6cSMauro Carvalho Chehab 	fe->discrete.denominator = is_ntsc ? 30000 : 25000;
16800aa77f6cSMauro Carvalho Chehab 	fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
16810aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator,
16820aa77f6cSMauro Carvalho Chehab 		fe->discrete.denominator);
16830aa77f6cSMauro Carvalho Chehab 	return 0;
16840aa77f6cSMauro Carvalho Chehab }
16850aa77f6cSMauro Carvalho Chehab 
16860aa77f6cSMauro Carvalho Chehab static int __s2255_open(struct file *file)
16870aa77f6cSMauro Carvalho Chehab {
16880aa77f6cSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
16890aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = video_drvdata(file);
16900aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
16910aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh;
16920aa77f6cSMauro Carvalho Chehab 	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
16930aa77f6cSMauro Carvalho Chehab 	int state;
16940aa77f6cSMauro Carvalho Chehab 	dprintk(1, "s2255: open called (dev=%s)\n",
16950aa77f6cSMauro Carvalho Chehab 		video_device_node_name(vdev));
16960aa77f6cSMauro Carvalho Chehab 	state = atomic_read(&dev->fw_data->fw_state);
16970aa77f6cSMauro Carvalho Chehab 	switch (state) {
16980aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
16990aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
17000aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
17010aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&dev->udev->dev,
17020aa77f6cSMauro Carvalho Chehab 			"firmware load failed. retrying.\n");
17030aa77f6cSMauro Carvalho Chehab 		s2255_fwload_start(dev, 1);
17040aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
17050aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
17060aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
17070aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
17080aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
17090aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
17100aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
17110aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
17120aa77f6cSMauro Carvalho Chehab 		break;
17130aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
17140aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
17150aa77f6cSMauro Carvalho Chehab 		/* give S2255_LOAD_TIMEOUT time for firmware to load in case
17160aa77f6cSMauro Carvalho Chehab 		   driver loaded and then device immediately opened */
17170aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "%s waiting for firmware load\n", __func__);
17180aa77f6cSMauro Carvalho Chehab 		wait_event_timeout(dev->fw_data->wait_fw,
17190aa77f6cSMauro Carvalho Chehab 				   ((atomic_read(&dev->fw_data->fw_state)
17200aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_SUCCESS) ||
17210aa77f6cSMauro Carvalho Chehab 				    (atomic_read(&dev->fw_data->fw_state)
17220aa77f6cSMauro Carvalho Chehab 				     == S2255_FW_DISCONNECTING)),
17230aa77f6cSMauro Carvalho Chehab 				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
17240aa77f6cSMauro Carvalho Chehab 		/* state may have changed, re-read */
17250aa77f6cSMauro Carvalho Chehab 		state = atomic_read(&dev->fw_data->fw_state);
17260aa77f6cSMauro Carvalho Chehab 		break;
17270aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
17280aa77f6cSMauro Carvalho Chehab 	default:
17290aa77f6cSMauro Carvalho Chehab 		break;
17300aa77f6cSMauro Carvalho Chehab 	}
17310aa77f6cSMauro Carvalho Chehab 	/* state may have changed in above switch statement */
17320aa77f6cSMauro Carvalho Chehab 	switch (state) {
17330aa77f6cSMauro Carvalho Chehab 	case S2255_FW_SUCCESS:
17340aa77f6cSMauro Carvalho Chehab 		break;
17350aa77f6cSMauro Carvalho Chehab 	case S2255_FW_FAILED:
17360aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "2255 firmware load failed.\n");
17370aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
17380aa77f6cSMauro Carvalho Chehab 	case S2255_FW_DISCONNECTING:
17390aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "%s: disconnecting\n", __func__);
17400aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
17410aa77f6cSMauro Carvalho Chehab 	case S2255_FW_LOADED_DSPWAIT:
17420aa77f6cSMauro Carvalho Chehab 	case S2255_FW_NOTLOADED:
17430aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "%s: firmware not loaded yet"
17440aa77f6cSMauro Carvalho Chehab 		       "please try again later\n",
17450aa77f6cSMauro Carvalho Chehab 		       __func__);
17460aa77f6cSMauro Carvalho Chehab 		/*
17470aa77f6cSMauro Carvalho Chehab 		 * Timeout on firmware load means device unusable.
17480aa77f6cSMauro Carvalho Chehab 		 * Set firmware failure state.
17490aa77f6cSMauro Carvalho Chehab 		 * On next s2255_open the firmware will be reloaded.
17500aa77f6cSMauro Carvalho Chehab 		 */
17510aa77f6cSMauro Carvalho Chehab 		atomic_set(&dev->fw_data->fw_state,
17520aa77f6cSMauro Carvalho Chehab 			   S2255_FW_FAILED);
17530aa77f6cSMauro Carvalho Chehab 		return -EAGAIN;
17540aa77f6cSMauro Carvalho Chehab 	default:
17550aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "%s: unknown state\n", __func__);
17560aa77f6cSMauro Carvalho Chehab 		return -EFAULT;
17570aa77f6cSMauro Carvalho Chehab 	}
17580aa77f6cSMauro Carvalho Chehab 	/* allocate + initialize per filehandle data */
17590aa77f6cSMauro Carvalho Chehab 	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
17600aa77f6cSMauro Carvalho Chehab 	if (NULL == fh)
17610aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
17620aa77f6cSMauro Carvalho Chehab 	file->private_data = fh;
17630aa77f6cSMauro Carvalho Chehab 	fh->dev = dev;
17640aa77f6cSMauro Carvalho Chehab 	fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
17650aa77f6cSMauro Carvalho Chehab 	fh->channel = channel;
17660aa77f6cSMauro Carvalho Chehab 	if (!channel->configured) {
17670aa77f6cSMauro Carvalho Chehab 		/* configure channel to default state */
17680aa77f6cSMauro Carvalho Chehab 		channel->fmt = &formats[0];
17690aa77f6cSMauro Carvalho Chehab 		s2255_set_mode(channel, &channel->mode);
17700aa77f6cSMauro Carvalho Chehab 		channel->configured = 1;
17710aa77f6cSMauro Carvalho Chehab 	}
17720aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s: dev=%s type=%s\n", __func__,
17730aa77f6cSMauro Carvalho Chehab 		video_device_node_name(vdev), v4l2_type_names[type]);
17740aa77f6cSMauro Carvalho Chehab 	dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__,
17750aa77f6cSMauro Carvalho Chehab 		(unsigned long)fh, (unsigned long)dev,
17760aa77f6cSMauro Carvalho Chehab 		(unsigned long)&channel->vidq);
17770aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s: list_empty active=%d\n", __func__,
17780aa77f6cSMauro Carvalho Chehab 		list_empty(&channel->vidq.active));
17790aa77f6cSMauro Carvalho Chehab 	videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
17800aa77f6cSMauro Carvalho Chehab 				    NULL, &dev->slock,
17810aa77f6cSMauro Carvalho Chehab 				    fh->type,
17820aa77f6cSMauro Carvalho Chehab 				    V4L2_FIELD_INTERLACED,
17830aa77f6cSMauro Carvalho Chehab 				    sizeof(struct s2255_buffer),
17840aa77f6cSMauro Carvalho Chehab 				    fh, vdev->lock);
17850aa77f6cSMauro Carvalho Chehab 	return 0;
17860aa77f6cSMauro Carvalho Chehab }
17870aa77f6cSMauro Carvalho Chehab 
17880aa77f6cSMauro Carvalho Chehab static int s2255_open(struct file *file)
17890aa77f6cSMauro Carvalho Chehab {
17900aa77f6cSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
17910aa77f6cSMauro Carvalho Chehab 	int ret;
17920aa77f6cSMauro Carvalho Chehab 
17930aa77f6cSMauro Carvalho Chehab 	if (mutex_lock_interruptible(vdev->lock))
17940aa77f6cSMauro Carvalho Chehab 		return -ERESTARTSYS;
17950aa77f6cSMauro Carvalho Chehab 	ret = __s2255_open(file);
17960aa77f6cSMauro Carvalho Chehab 	mutex_unlock(vdev->lock);
17970aa77f6cSMauro Carvalho Chehab 	return ret;
17980aa77f6cSMauro Carvalho Chehab }
17990aa77f6cSMauro Carvalho Chehab 
18000aa77f6cSMauro Carvalho Chehab static unsigned int s2255_poll(struct file *file,
18010aa77f6cSMauro Carvalho Chehab 			       struct poll_table_struct *wait)
18020aa77f6cSMauro Carvalho Chehab {
18030aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
18040aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
18050aa77f6cSMauro Carvalho Chehab 	int rc;
18060aa77f6cSMauro Carvalho Chehab 	dprintk(100, "%s\n", __func__);
18070aa77f6cSMauro Carvalho Chehab 	if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
18080aa77f6cSMauro Carvalho Chehab 		return POLLERR;
18090aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
18100aa77f6cSMauro Carvalho Chehab 	rc = videobuf_poll_stream(file, &fh->vb_vidq, wait);
18110aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
18120aa77f6cSMauro Carvalho Chehab 	return rc;
18130aa77f6cSMauro Carvalho Chehab }
18140aa77f6cSMauro Carvalho Chehab 
18150aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev)
18160aa77f6cSMauro Carvalho Chehab {
18170aa77f6cSMauro Carvalho Chehab 	/* board shutdown stops the read pipe if it is running */
18180aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
18190aa77f6cSMauro Carvalho Chehab 	/* make sure firmware still not trying to load */
18200aa77f6cSMauro Carvalho Chehab 	del_timer(&dev->timer);  /* only started in .probe and .open */
18210aa77f6cSMauro Carvalho Chehab 	if (dev->fw_data->fw_urb) {
18220aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(dev->fw_data->fw_urb);
18230aa77f6cSMauro Carvalho Chehab 		usb_free_urb(dev->fw_data->fw_urb);
18240aa77f6cSMauro Carvalho Chehab 		dev->fw_data->fw_urb = NULL;
18250aa77f6cSMauro Carvalho Chehab 	}
18260aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
18270aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
18280aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
18290aa77f6cSMauro Carvalho Chehab 	/* reset the DSP so firmware can be reloaded next time */
18300aa77f6cSMauro Carvalho Chehab 	s2255_reset_dsppower(dev);
18310aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
18320aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
18330aa77f6cSMauro Carvalho Chehab 	v4l2_device_unregister(&dev->v4l2_dev);
18340aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s", __func__);
18350aa77f6cSMauro Carvalho Chehab 	kfree(dev);
18360aa77f6cSMauro Carvalho Chehab }
18370aa77f6cSMauro Carvalho Chehab 
18380aa77f6cSMauro Carvalho Chehab static int s2255_release(struct file *file)
18390aa77f6cSMauro Carvalho Chehab {
18400aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
18410aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = fh->dev;
18420aa77f6cSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
18430aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel = fh->channel;
18440aa77f6cSMauro Carvalho Chehab 	if (!dev)
18450aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
18460aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
18470aa77f6cSMauro Carvalho Chehab 	/* turn off stream */
18480aa77f6cSMauro Carvalho Chehab 	if (res_check(fh)) {
18490aa77f6cSMauro Carvalho Chehab 		if (channel->b_acquire)
18500aa77f6cSMauro Carvalho Chehab 			s2255_stop_acquire(fh->channel);
18510aa77f6cSMauro Carvalho Chehab 		videobuf_streamoff(&fh->vb_vidq);
18520aa77f6cSMauro Carvalho Chehab 		res_free(fh);
18530aa77f6cSMauro Carvalho Chehab 	}
18540aa77f6cSMauro Carvalho Chehab 	videobuf_mmap_free(&fh->vb_vidq);
18550aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
18560aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev));
18570aa77f6cSMauro Carvalho Chehab 	kfree(fh);
18580aa77f6cSMauro Carvalho Chehab 	return 0;
18590aa77f6cSMauro Carvalho Chehab }
18600aa77f6cSMauro Carvalho Chehab 
18610aa77f6cSMauro Carvalho Chehab static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
18620aa77f6cSMauro Carvalho Chehab {
18630aa77f6cSMauro Carvalho Chehab 	struct s2255_fh *fh = file->private_data;
1864e839776fSJulia Lawall 	struct s2255_dev *dev;
18650aa77f6cSMauro Carvalho Chehab 	int ret;
18660aa77f6cSMauro Carvalho Chehab 
18670aa77f6cSMauro Carvalho Chehab 	if (!fh)
18680aa77f6cSMauro Carvalho Chehab 		return -ENODEV;
1869e839776fSJulia Lawall 	dev = fh->dev;
18700aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma);
18710aa77f6cSMauro Carvalho Chehab 	if (mutex_lock_interruptible(&dev->lock))
18720aa77f6cSMauro Carvalho Chehab 		return -ERESTARTSYS;
18730aa77f6cSMauro Carvalho Chehab 	ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
18740aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
18750aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__,
18760aa77f6cSMauro Carvalho Chehab 		(unsigned long)vma->vm_start,
18770aa77f6cSMauro Carvalho Chehab 		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
18780aa77f6cSMauro Carvalho Chehab 	return ret;
18790aa77f6cSMauro Carvalho Chehab }
18800aa77f6cSMauro Carvalho Chehab 
18810aa77f6cSMauro Carvalho Chehab static const struct v4l2_file_operations s2255_fops_v4l = {
18820aa77f6cSMauro Carvalho Chehab 	.owner = THIS_MODULE,
18830aa77f6cSMauro Carvalho Chehab 	.open = s2255_open,
18840aa77f6cSMauro Carvalho Chehab 	.release = s2255_release,
18850aa77f6cSMauro Carvalho Chehab 	.poll = s2255_poll,
18860aa77f6cSMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,	/* V4L2 ioctl handler */
18870aa77f6cSMauro Carvalho Chehab 	.mmap = s2255_mmap_v4l,
18880aa77f6cSMauro Carvalho Chehab };
18890aa77f6cSMauro Carvalho Chehab 
18900aa77f6cSMauro Carvalho Chehab static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
18910aa77f6cSMauro Carvalho Chehab 	.vidioc_querymenu = vidioc_querymenu,
18920aa77f6cSMauro Carvalho Chehab 	.vidioc_querycap = vidioc_querycap,
18930aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
18940aa77f6cSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
18950aa77f6cSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
18960aa77f6cSMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
18970aa77f6cSMauro Carvalho Chehab 	.vidioc_reqbufs = vidioc_reqbufs,
18980aa77f6cSMauro Carvalho Chehab 	.vidioc_querybuf = vidioc_querybuf,
18990aa77f6cSMauro Carvalho Chehab 	.vidioc_qbuf = vidioc_qbuf,
19000aa77f6cSMauro Carvalho Chehab 	.vidioc_dqbuf = vidioc_dqbuf,
19010aa77f6cSMauro Carvalho Chehab 	.vidioc_s_std = vidioc_s_std,
19020aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_input = vidioc_enum_input,
19030aa77f6cSMauro Carvalho Chehab 	.vidioc_g_input = vidioc_g_input,
19040aa77f6cSMauro Carvalho Chehab 	.vidioc_s_input = vidioc_s_input,
19050aa77f6cSMauro Carvalho Chehab 	.vidioc_queryctrl = vidioc_queryctrl,
19060aa77f6cSMauro Carvalho Chehab 	.vidioc_g_ctrl = vidioc_g_ctrl,
19070aa77f6cSMauro Carvalho Chehab 	.vidioc_s_ctrl = vidioc_s_ctrl,
19080aa77f6cSMauro Carvalho Chehab 	.vidioc_streamon = vidioc_streamon,
19090aa77f6cSMauro Carvalho Chehab 	.vidioc_streamoff = vidioc_streamoff,
19100aa77f6cSMauro Carvalho Chehab 	.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
19110aa77f6cSMauro Carvalho Chehab 	.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
19120aa77f6cSMauro Carvalho Chehab 	.vidioc_s_parm = vidioc_s_parm,
19130aa77f6cSMauro Carvalho Chehab 	.vidioc_g_parm = vidioc_g_parm,
19140aa77f6cSMauro Carvalho Chehab 	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
19150aa77f6cSMauro Carvalho Chehab };
19160aa77f6cSMauro Carvalho Chehab 
19170aa77f6cSMauro Carvalho Chehab static void s2255_video_device_release(struct video_device *vdev)
19180aa77f6cSMauro Carvalho Chehab {
19190aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
19200aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s, chnls: %d \n", __func__,
19210aa77f6cSMauro Carvalho Chehab 		atomic_read(&dev->num_channels));
19220aa77f6cSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->num_channels))
19230aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
19240aa77f6cSMauro Carvalho Chehab 	return;
19250aa77f6cSMauro Carvalho Chehab }
19260aa77f6cSMauro Carvalho Chehab 
19270aa77f6cSMauro Carvalho Chehab static struct video_device template = {
19280aa77f6cSMauro Carvalho Chehab 	.name = "s2255v",
19290aa77f6cSMauro Carvalho Chehab 	.fops = &s2255_fops_v4l,
19300aa77f6cSMauro Carvalho Chehab 	.ioctl_ops = &s2255_ioctl_ops,
19310aa77f6cSMauro Carvalho Chehab 	.release = s2255_video_device_release,
19320aa77f6cSMauro Carvalho Chehab 	.tvnorms = S2255_NORMS,
19330aa77f6cSMauro Carvalho Chehab 	.current_norm = V4L2_STD_NTSC_M,
19340aa77f6cSMauro Carvalho Chehab };
19350aa77f6cSMauro Carvalho Chehab 
19360aa77f6cSMauro Carvalho Chehab static int s2255_probe_v4l(struct s2255_dev *dev)
19370aa77f6cSMauro Carvalho Chehab {
19380aa77f6cSMauro Carvalho Chehab 	int ret;
19390aa77f6cSMauro Carvalho Chehab 	int i;
19400aa77f6cSMauro Carvalho Chehab 	int cur_nr = video_nr;
19410aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel;
19420aa77f6cSMauro Carvalho Chehab 	ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
19430aa77f6cSMauro Carvalho Chehab 	if (ret)
19440aa77f6cSMauro Carvalho Chehab 		return ret;
19450aa77f6cSMauro Carvalho Chehab 	/* initialize all video 4 linux */
19460aa77f6cSMauro Carvalho Chehab 	/* register 4 video devices */
19470aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
19480aa77f6cSMauro Carvalho Chehab 		channel = &dev->channel[i];
19490aa77f6cSMauro Carvalho Chehab 		INIT_LIST_HEAD(&channel->vidq.active);
19500aa77f6cSMauro Carvalho Chehab 		channel->vidq.dev = dev;
19510aa77f6cSMauro Carvalho Chehab 		/* register 4 video devices */
19520aa77f6cSMauro Carvalho Chehab 		channel->vdev = template;
19530aa77f6cSMauro Carvalho Chehab 		channel->vdev.lock = &dev->lock;
19540aa77f6cSMauro Carvalho Chehab 		channel->vdev.v4l2_dev = &dev->v4l2_dev;
19550aa77f6cSMauro Carvalho Chehab 		video_set_drvdata(&channel->vdev, channel);
19560aa77f6cSMauro Carvalho Chehab 		if (video_nr == -1)
19570aa77f6cSMauro Carvalho Chehab 			ret = video_register_device(&channel->vdev,
19580aa77f6cSMauro Carvalho Chehab 						    VFL_TYPE_GRABBER,
19590aa77f6cSMauro Carvalho Chehab 						    video_nr);
19600aa77f6cSMauro Carvalho Chehab 		else
19610aa77f6cSMauro Carvalho Chehab 			ret = video_register_device(&channel->vdev,
19620aa77f6cSMauro Carvalho Chehab 						    VFL_TYPE_GRABBER,
19630aa77f6cSMauro Carvalho Chehab 						    cur_nr + i);
19640aa77f6cSMauro Carvalho Chehab 
19650aa77f6cSMauro Carvalho Chehab 		if (ret) {
19660aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev,
19670aa77f6cSMauro Carvalho Chehab 				"failed to register video device!\n");
19680aa77f6cSMauro Carvalho Chehab 			break;
19690aa77f6cSMauro Carvalho Chehab 		}
19700aa77f6cSMauro Carvalho Chehab 		atomic_inc(&dev->num_channels);
19710aa77f6cSMauro Carvalho Chehab 		v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
19720aa77f6cSMauro Carvalho Chehab 			  video_device_node_name(&channel->vdev));
19730aa77f6cSMauro Carvalho Chehab 
19740aa77f6cSMauro Carvalho Chehab 	}
19750aa77f6cSMauro Carvalho Chehab 	printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
19760aa77f6cSMauro Carvalho Chehab 	       S2255_VERSION);
19770aa77f6cSMauro Carvalho Chehab 	/* if no channels registered, return error and probe will fail*/
19780aa77f6cSMauro Carvalho Chehab 	if (atomic_read(&dev->num_channels) == 0) {
19790aa77f6cSMauro Carvalho Chehab 		v4l2_device_unregister(&dev->v4l2_dev);
19800aa77f6cSMauro Carvalho Chehab 		return ret;
19810aa77f6cSMauro Carvalho Chehab 	}
19820aa77f6cSMauro Carvalho Chehab 	if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
19830aa77f6cSMauro Carvalho Chehab 		printk(KERN_WARNING "s2255: Not all channels available.\n");
19840aa77f6cSMauro Carvalho Chehab 	return 0;
19850aa77f6cSMauro Carvalho Chehab }
19860aa77f6cSMauro Carvalho Chehab 
19870aa77f6cSMauro Carvalho Chehab /* this function moves the usb stream read pipe data
19880aa77f6cSMauro Carvalho Chehab  * into the system buffers.
19890aa77f6cSMauro Carvalho Chehab  * returns 0 on success, EAGAIN if more data to process( call this
19900aa77f6cSMauro Carvalho Chehab  * function again).
19910aa77f6cSMauro Carvalho Chehab  *
19920aa77f6cSMauro Carvalho Chehab  * Received frame structure:
19930aa77f6cSMauro Carvalho Chehab  * bytes 0-3:  marker : 0x2255DA4AL (S2255_MARKER_FRAME)
19940aa77f6cSMauro Carvalho Chehab  * bytes 4-7:  channel: 0-3
19950aa77f6cSMauro Carvalho Chehab  * bytes 8-11: payload size:  size of the frame
19960aa77f6cSMauro Carvalho Chehab  * bytes 12-payloadsize+12:  frame data
19970aa77f6cSMauro Carvalho Chehab  */
19980aa77f6cSMauro Carvalho Chehab static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
19990aa77f6cSMauro Carvalho Chehab {
20000aa77f6cSMauro Carvalho Chehab 	char *pdest;
20010aa77f6cSMauro Carvalho Chehab 	u32 offset = 0;
20020aa77f6cSMauro Carvalho Chehab 	int bframe = 0;
20030aa77f6cSMauro Carvalho Chehab 	char *psrc;
20040aa77f6cSMauro Carvalho Chehab 	unsigned long copy_size;
20050aa77f6cSMauro Carvalho Chehab 	unsigned long size;
20060aa77f6cSMauro Carvalho Chehab 	s32 idx = -1;
20070aa77f6cSMauro Carvalho Chehab 	struct s2255_framei *frm;
20080aa77f6cSMauro Carvalho Chehab 	unsigned char *pdata;
20090aa77f6cSMauro Carvalho Chehab 	struct s2255_channel *channel;
20100aa77f6cSMauro Carvalho Chehab 	dprintk(100, "buffer to user\n");
20110aa77f6cSMauro Carvalho Chehab 	channel = &dev->channel[dev->cc];
20120aa77f6cSMauro Carvalho Chehab 	idx = channel->cur_frame;
20130aa77f6cSMauro Carvalho Chehab 	frm = &channel->buffer.frame[idx];
20140aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
20150aa77f6cSMauro Carvalho Chehab 		int jj;
20160aa77f6cSMauro Carvalho Chehab 		unsigned int cc;
20170aa77f6cSMauro Carvalho Chehab 		__le32 *pdword; /*data from dsp is little endian */
20180aa77f6cSMauro Carvalho Chehab 		int payload;
20190aa77f6cSMauro Carvalho Chehab 		/* search for marker codes */
20200aa77f6cSMauro Carvalho Chehab 		pdata = (unsigned char *)pipe_info->transfer_buffer;
20210aa77f6cSMauro Carvalho Chehab 		pdword = (__le32 *)pdata;
20220aa77f6cSMauro Carvalho Chehab 		for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
20230aa77f6cSMauro Carvalho Chehab 			switch (*pdword) {
20240aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_FRAME:
20250aa77f6cSMauro Carvalho Chehab 				dprintk(4, "found frame marker at offset:"
20260aa77f6cSMauro Carvalho Chehab 					" %d [%x %x]\n", jj, pdata[0],
20270aa77f6cSMauro Carvalho Chehab 					pdata[1]);
20280aa77f6cSMauro Carvalho Chehab 				offset = jj + PREFIX_SIZE;
20290aa77f6cSMauro Carvalho Chehab 				bframe = 1;
20300aa77f6cSMauro Carvalho Chehab 				cc = le32_to_cpu(pdword[1]);
20310aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS) {
20320aa77f6cSMauro Carvalho Chehab 					printk(KERN_ERR
20330aa77f6cSMauro Carvalho Chehab 					       "bad channel\n");
20340aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
20350aa77f6cSMauro Carvalho Chehab 				}
20360aa77f6cSMauro Carvalho Chehab 				/* reverse it */
20370aa77f6cSMauro Carvalho Chehab 				dev->cc = G_chnmap[cc];
20380aa77f6cSMauro Carvalho Chehab 				channel = &dev->channel[dev->cc];
20390aa77f6cSMauro Carvalho Chehab 				payload =  le32_to_cpu(pdword[3]);
20400aa77f6cSMauro Carvalho Chehab 				if (payload > channel->req_image_size) {
20410aa77f6cSMauro Carvalho Chehab 					channel->bad_payload++;
20420aa77f6cSMauro Carvalho Chehab 					/* discard the bad frame */
20430aa77f6cSMauro Carvalho Chehab 					return -EINVAL;
20440aa77f6cSMauro Carvalho Chehab 				}
20450aa77f6cSMauro Carvalho Chehab 				channel->pkt_size = payload;
20460aa77f6cSMauro Carvalho Chehab 				channel->jpg_size = le32_to_cpu(pdword[4]);
20470aa77f6cSMauro Carvalho Chehab 				break;
20480aa77f6cSMauro Carvalho Chehab 			case S2255_MARKER_RESPONSE:
20490aa77f6cSMauro Carvalho Chehab 
20500aa77f6cSMauro Carvalho Chehab 				pdata += DEF_USB_BLOCK;
20510aa77f6cSMauro Carvalho Chehab 				jj += DEF_USB_BLOCK;
20520aa77f6cSMauro Carvalho Chehab 				if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
20530aa77f6cSMauro Carvalho Chehab 					break;
20540aa77f6cSMauro Carvalho Chehab 				cc = G_chnmap[le32_to_cpu(pdword[1])];
20550aa77f6cSMauro Carvalho Chehab 				if (cc >= MAX_CHANNELS)
20560aa77f6cSMauro Carvalho Chehab 					break;
20570aa77f6cSMauro Carvalho Chehab 				channel = &dev->channel[cc];
20580aa77f6cSMauro Carvalho Chehab 				switch (pdword[2]) {
20590aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_SETMODE:
20600aa77f6cSMauro Carvalho Chehab 					/* check if channel valid */
20610aa77f6cSMauro Carvalho Chehab 					/* set mode ready */
20620aa77f6cSMauro Carvalho Chehab 					channel->setmode_ready = 1;
20630aa77f6cSMauro Carvalho Chehab 					wake_up(&channel->wait_setmode);
20640aa77f6cSMauro Carvalho Chehab 					dprintk(5, "setmode ready %d\n", cc);
20650aa77f6cSMauro Carvalho Chehab 					break;
20660aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_FW:
20670aa77f6cSMauro Carvalho Chehab 					dev->chn_ready |= (1 << cc);
20680aa77f6cSMauro Carvalho Chehab 					if ((dev->chn_ready & 0x0f) != 0x0f)
20690aa77f6cSMauro Carvalho Chehab 						break;
20700aa77f6cSMauro Carvalho Chehab 					/* all channels ready */
20710aa77f6cSMauro Carvalho Chehab 					printk(KERN_INFO "s2255: fw loaded\n");
20720aa77f6cSMauro Carvalho Chehab 					atomic_set(&dev->fw_data->fw_state,
20730aa77f6cSMauro Carvalho Chehab 						   S2255_FW_SUCCESS);
20740aa77f6cSMauro Carvalho Chehab 					wake_up(&dev->fw_data->wait_fw);
20750aa77f6cSMauro Carvalho Chehab 					break;
20760aa77f6cSMauro Carvalho Chehab 				case S2255_RESPONSE_STATUS:
20770aa77f6cSMauro Carvalho Chehab 					channel->vidstatus = le32_to_cpu(pdword[3]);
20780aa77f6cSMauro Carvalho Chehab 					channel->vidstatus_ready = 1;
20790aa77f6cSMauro Carvalho Chehab 					wake_up(&channel->wait_vidstatus);
20800aa77f6cSMauro Carvalho Chehab 					dprintk(5, "got vidstatus %x chan %d\n",
20810aa77f6cSMauro Carvalho Chehab 						le32_to_cpu(pdword[3]), cc);
20820aa77f6cSMauro Carvalho Chehab 					break;
20830aa77f6cSMauro Carvalho Chehab 				default:
20840aa77f6cSMauro Carvalho Chehab 					printk(KERN_INFO "s2255 unknown resp\n");
20850aa77f6cSMauro Carvalho Chehab 				}
20860aa77f6cSMauro Carvalho Chehab 			default:
20870aa77f6cSMauro Carvalho Chehab 				pdata++;
20880aa77f6cSMauro Carvalho Chehab 				break;
20890aa77f6cSMauro Carvalho Chehab 			}
20900aa77f6cSMauro Carvalho Chehab 			if (bframe)
20910aa77f6cSMauro Carvalho Chehab 				break;
20920aa77f6cSMauro Carvalho Chehab 		} /* for */
20930aa77f6cSMauro Carvalho Chehab 		if (!bframe)
20940aa77f6cSMauro Carvalho Chehab 			return -EINVAL;
20950aa77f6cSMauro Carvalho Chehab 	}
20960aa77f6cSMauro Carvalho Chehab 	channel = &dev->channel[dev->cc];
20970aa77f6cSMauro Carvalho Chehab 	idx = channel->cur_frame;
20980aa77f6cSMauro Carvalho Chehab 	frm = &channel->buffer.frame[idx];
20990aa77f6cSMauro Carvalho Chehab 	/* search done.  now find out if should be acquiring on this channel */
21000aa77f6cSMauro Carvalho Chehab 	if (!channel->b_acquire) {
21010aa77f6cSMauro Carvalho Chehab 		/* we found a frame, but this channel is turned off */
21020aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
21030aa77f6cSMauro Carvalho Chehab 		return -EINVAL;
21040aa77f6cSMauro Carvalho Chehab 	}
21050aa77f6cSMauro Carvalho Chehab 
21060aa77f6cSMauro Carvalho Chehab 	if (frm->ulState == S2255_READ_IDLE) {
21070aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_FRAME;
21080aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
21090aa77f6cSMauro Carvalho Chehab 	}
21100aa77f6cSMauro Carvalho Chehab 
21110aa77f6cSMauro Carvalho Chehab 	/* skip the marker 512 bytes (and offset if out of sync) */
21120aa77f6cSMauro Carvalho Chehab 	psrc = (u8 *)pipe_info->transfer_buffer + offset;
21130aa77f6cSMauro Carvalho Chehab 
21140aa77f6cSMauro Carvalho Chehab 
21150aa77f6cSMauro Carvalho Chehab 	if (frm->lpvbits == NULL) {
21160aa77f6cSMauro Carvalho Chehab 		dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
21170aa77f6cSMauro Carvalho Chehab 			frm, dev, dev->cc, idx);
21180aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
21190aa77f6cSMauro Carvalho Chehab 	}
21200aa77f6cSMauro Carvalho Chehab 
21210aa77f6cSMauro Carvalho Chehab 	pdest = frm->lpvbits + frm->cur_size;
21220aa77f6cSMauro Carvalho Chehab 
21230aa77f6cSMauro Carvalho Chehab 	copy_size = (pipe_info->cur_transfer_size - offset);
21240aa77f6cSMauro Carvalho Chehab 
21250aa77f6cSMauro Carvalho Chehab 	size = channel->pkt_size - PREFIX_SIZE;
21260aa77f6cSMauro Carvalho Chehab 
21270aa77f6cSMauro Carvalho Chehab 	/* sanity check on pdest */
21280aa77f6cSMauro Carvalho Chehab 	if ((copy_size + frm->cur_size) < channel->req_image_size)
21290aa77f6cSMauro Carvalho Chehab 		memcpy(pdest, psrc, copy_size);
21300aa77f6cSMauro Carvalho Chehab 
21310aa77f6cSMauro Carvalho Chehab 	frm->cur_size += copy_size;
21320aa77f6cSMauro Carvalho Chehab 	dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size);
21330aa77f6cSMauro Carvalho Chehab 
21340aa77f6cSMauro Carvalho Chehab 	if (frm->cur_size >= size) {
21350aa77f6cSMauro Carvalho Chehab 		dprintk(2, "****************[%d]Buffer[%d]full*************\n",
21360aa77f6cSMauro Carvalho Chehab 			dev->cc, idx);
21370aa77f6cSMauro Carvalho Chehab 		channel->last_frame = channel->cur_frame;
21380aa77f6cSMauro Carvalho Chehab 		channel->cur_frame++;
21390aa77f6cSMauro Carvalho Chehab 		/* end of system frame ring buffer, start at zero */
21400aa77f6cSMauro Carvalho Chehab 		if ((channel->cur_frame == SYS_FRAMES) ||
21410aa77f6cSMauro Carvalho Chehab 		    (channel->cur_frame == channel->buffer.dwFrames))
21420aa77f6cSMauro Carvalho Chehab 			channel->cur_frame = 0;
21430aa77f6cSMauro Carvalho Chehab 		/* frame ready */
21440aa77f6cSMauro Carvalho Chehab 		if (channel->b_acquire)
21450aa77f6cSMauro Carvalho Chehab 			s2255_got_frame(channel, channel->jpg_size);
21460aa77f6cSMauro Carvalho Chehab 		channel->frame_count++;
21470aa77f6cSMauro Carvalho Chehab 		frm->ulState = S2255_READ_IDLE;
21480aa77f6cSMauro Carvalho Chehab 		frm->cur_size = 0;
21490aa77f6cSMauro Carvalho Chehab 
21500aa77f6cSMauro Carvalho Chehab 	}
21510aa77f6cSMauro Carvalho Chehab 	/* done successfully */
21520aa77f6cSMauro Carvalho Chehab 	return 0;
21530aa77f6cSMauro Carvalho Chehab }
21540aa77f6cSMauro Carvalho Chehab 
21550aa77f6cSMauro Carvalho Chehab static void s2255_read_video_callback(struct s2255_dev *dev,
21560aa77f6cSMauro Carvalho Chehab 				      struct s2255_pipeinfo *pipe_info)
21570aa77f6cSMauro Carvalho Chehab {
21580aa77f6cSMauro Carvalho Chehab 	int res;
21590aa77f6cSMauro Carvalho Chehab 	dprintk(50, "callback read video \n");
21600aa77f6cSMauro Carvalho Chehab 
21610aa77f6cSMauro Carvalho Chehab 	if (dev->cc >= MAX_CHANNELS) {
21620aa77f6cSMauro Carvalho Chehab 		dev->cc = 0;
21630aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "invalid channel\n");
21640aa77f6cSMauro Carvalho Chehab 		return;
21650aa77f6cSMauro Carvalho Chehab 	}
21660aa77f6cSMauro Carvalho Chehab 	/* otherwise copy to the system buffers */
21670aa77f6cSMauro Carvalho Chehab 	res = save_frame(dev, pipe_info);
21680aa77f6cSMauro Carvalho Chehab 	if (res != 0)
21690aa77f6cSMauro Carvalho Chehab 		dprintk(4, "s2255: read callback failed\n");
21700aa77f6cSMauro Carvalho Chehab 
21710aa77f6cSMauro Carvalho Chehab 	dprintk(50, "callback read video done\n");
21720aa77f6cSMauro Carvalho Chehab 	return;
21730aa77f6cSMauro Carvalho Chehab }
21740aa77f6cSMauro Carvalho Chehab 
21750aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
21760aa77f6cSMauro Carvalho Chehab 			     u16 Index, u16 Value, void *TransferBuffer,
21770aa77f6cSMauro Carvalho Chehab 			     s32 TransferBufferLength, int bOut)
21780aa77f6cSMauro Carvalho Chehab {
21790aa77f6cSMauro Carvalho Chehab 	int r;
21800aa77f6cSMauro Carvalho Chehab 	if (!bOut) {
21810aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
21820aa77f6cSMauro Carvalho Chehab 				    Request,
21830aa77f6cSMauro Carvalho Chehab 				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
21840aa77f6cSMauro Carvalho Chehab 				    USB_DIR_IN,
21850aa77f6cSMauro Carvalho Chehab 				    Value, Index, TransferBuffer,
21860aa77f6cSMauro Carvalho Chehab 				    TransferBufferLength, HZ * 5);
21870aa77f6cSMauro Carvalho Chehab 	} else {
21880aa77f6cSMauro Carvalho Chehab 		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
21890aa77f6cSMauro Carvalho Chehab 				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
21900aa77f6cSMauro Carvalho Chehab 				    Value, Index, TransferBuffer,
21910aa77f6cSMauro Carvalho Chehab 				    TransferBufferLength, HZ * 5);
21920aa77f6cSMauro Carvalho Chehab 	}
21930aa77f6cSMauro Carvalho Chehab 	return r;
21940aa77f6cSMauro Carvalho Chehab }
21950aa77f6cSMauro Carvalho Chehab 
21960aa77f6cSMauro Carvalho Chehab /*
21970aa77f6cSMauro Carvalho Chehab  * retrieve FX2 firmware version. future use.
21980aa77f6cSMauro Carvalho Chehab  * @param dev pointer to device extension
21990aa77f6cSMauro Carvalho Chehab  * @return -1 for fail, else returns firmware version as an int(16 bits)
22000aa77f6cSMauro Carvalho Chehab  */
22010aa77f6cSMauro Carvalho Chehab static int s2255_get_fx2fw(struct s2255_dev *dev)
22020aa77f6cSMauro Carvalho Chehab {
22030aa77f6cSMauro Carvalho Chehab 	int fw;
22040aa77f6cSMauro Carvalho Chehab 	int ret;
22050aa77f6cSMauro Carvalho Chehab 	unsigned char transBuffer[64];
22060aa77f6cSMauro Carvalho Chehab 	ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
22070aa77f6cSMauro Carvalho Chehab 			       S2255_VR_IN);
22080aa77f6cSMauro Carvalho Chehab 	if (ret < 0)
22090aa77f6cSMauro Carvalho Chehab 		dprintk(2, "get fw error: %x\n", ret);
22100aa77f6cSMauro Carvalho Chehab 	fw = transBuffer[0] + (transBuffer[1] << 8);
22110aa77f6cSMauro Carvalho Chehab 	dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
22120aa77f6cSMauro Carvalho Chehab 	return fw;
22130aa77f6cSMauro Carvalho Chehab }
22140aa77f6cSMauro Carvalho Chehab 
22150aa77f6cSMauro Carvalho Chehab /*
22160aa77f6cSMauro Carvalho Chehab  * Create the system ring buffer to copy frames into from the
22170aa77f6cSMauro Carvalho Chehab  * usb read pipe.
22180aa77f6cSMauro Carvalho Chehab  */
22190aa77f6cSMauro Carvalho Chehab static int s2255_create_sys_buffers(struct s2255_channel *channel)
22200aa77f6cSMauro Carvalho Chehab {
22210aa77f6cSMauro Carvalho Chehab 	unsigned long i;
22220aa77f6cSMauro Carvalho Chehab 	unsigned long reqsize;
22230aa77f6cSMauro Carvalho Chehab 	dprintk(1, "create sys buffers\n");
22240aa77f6cSMauro Carvalho Chehab 	channel->buffer.dwFrames = SYS_FRAMES;
22250aa77f6cSMauro Carvalho Chehab 	/* always allocate maximum size(PAL) for system buffers */
22260aa77f6cSMauro Carvalho Chehab 	reqsize = SYS_FRAMES_MAXSIZE;
22270aa77f6cSMauro Carvalho Chehab 
22280aa77f6cSMauro Carvalho Chehab 	if (reqsize > SYS_FRAMES_MAXSIZE)
22290aa77f6cSMauro Carvalho Chehab 		reqsize = SYS_FRAMES_MAXSIZE;
22300aa77f6cSMauro Carvalho Chehab 
22310aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
22320aa77f6cSMauro Carvalho Chehab 		/* allocate the frames */
22330aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[i].lpvbits = vmalloc(reqsize);
22340aa77f6cSMauro Carvalho Chehab 		dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n",
22350aa77f6cSMauro Carvalho Chehab 			&channel->buffer.frame[i], channel->idx, i,
22360aa77f6cSMauro Carvalho Chehab 			channel->buffer.frame[i].lpvbits);
22370aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[i].size = reqsize;
22380aa77f6cSMauro Carvalho Chehab 		if (channel->buffer.frame[i].lpvbits == NULL) {
22390aa77f6cSMauro Carvalho Chehab 			printk(KERN_INFO "out of memory.  using less frames\n");
22400aa77f6cSMauro Carvalho Chehab 			channel->buffer.dwFrames = i;
22410aa77f6cSMauro Carvalho Chehab 			break;
22420aa77f6cSMauro Carvalho Chehab 		}
22430aa77f6cSMauro Carvalho Chehab 	}
22440aa77f6cSMauro Carvalho Chehab 
22450aa77f6cSMauro Carvalho Chehab 	/* make sure internal states are set */
22460aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
22470aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[i].ulState = 0;
22480aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[i].cur_size = 0;
22490aa77f6cSMauro Carvalho Chehab 	}
22500aa77f6cSMauro Carvalho Chehab 
22510aa77f6cSMauro Carvalho Chehab 	channel->cur_frame = 0;
22520aa77f6cSMauro Carvalho Chehab 	channel->last_frame = -1;
22530aa77f6cSMauro Carvalho Chehab 	return 0;
22540aa77f6cSMauro Carvalho Chehab }
22550aa77f6cSMauro Carvalho Chehab 
22560aa77f6cSMauro Carvalho Chehab static int s2255_release_sys_buffers(struct s2255_channel *channel)
22570aa77f6cSMauro Carvalho Chehab {
22580aa77f6cSMauro Carvalho Chehab 	unsigned long i;
22590aa77f6cSMauro Carvalho Chehab 	dprintk(1, "release sys buffers\n");
22600aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < SYS_FRAMES; i++) {
22610aa77f6cSMauro Carvalho Chehab 		if (channel->buffer.frame[i].lpvbits) {
22620aa77f6cSMauro Carvalho Chehab 			dprintk(1, "vfree %p\n",
22630aa77f6cSMauro Carvalho Chehab 				channel->buffer.frame[i].lpvbits);
22640aa77f6cSMauro Carvalho Chehab 			vfree(channel->buffer.frame[i].lpvbits);
22650aa77f6cSMauro Carvalho Chehab 		}
22660aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[i].lpvbits = NULL;
22670aa77f6cSMauro Carvalho Chehab 	}
22680aa77f6cSMauro Carvalho Chehab 	return 0;
22690aa77f6cSMauro Carvalho Chehab }
22700aa77f6cSMauro Carvalho Chehab 
22710aa77f6cSMauro Carvalho Chehab static int s2255_board_init(struct s2255_dev *dev)
22720aa77f6cSMauro Carvalho Chehab {
22730aa77f6cSMauro Carvalho Chehab 	struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
22740aa77f6cSMauro Carvalho Chehab 	int fw_ver;
22750aa77f6cSMauro Carvalho Chehab 	int j;
22760aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
22770aa77f6cSMauro Carvalho Chehab 	dprintk(4, "board init: %p", dev);
22780aa77f6cSMauro Carvalho Chehab 	memset(pipe, 0, sizeof(*pipe));
22790aa77f6cSMauro Carvalho Chehab 	pipe->dev = dev;
22800aa77f6cSMauro Carvalho Chehab 	pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
22810aa77f6cSMauro Carvalho Chehab 	pipe->max_transfer_size = S2255_USB_XFER_SIZE;
22820aa77f6cSMauro Carvalho Chehab 
22830aa77f6cSMauro Carvalho Chehab 	pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
22840aa77f6cSMauro Carvalho Chehab 					GFP_KERNEL);
22850aa77f6cSMauro Carvalho Chehab 	if (pipe->transfer_buffer == NULL) {
22860aa77f6cSMauro Carvalho Chehab 		dprintk(1, "out of memory!\n");
22870aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
22880aa77f6cSMauro Carvalho Chehab 	}
22890aa77f6cSMauro Carvalho Chehab 	/* query the firmware */
22900aa77f6cSMauro Carvalho Chehab 	fw_ver = s2255_get_fx2fw(dev);
22910aa77f6cSMauro Carvalho Chehab 
22920aa77f6cSMauro Carvalho Chehab 	printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
22930aa77f6cSMauro Carvalho Chehab 	       (fw_ver >> 8) & 0xff,
22940aa77f6cSMauro Carvalho Chehab 	       fw_ver & 0xff);
22950aa77f6cSMauro Carvalho Chehab 
22960aa77f6cSMauro Carvalho Chehab 	if (fw_ver < S2255_CUR_USB_FWVER)
22970aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "s2255: newer USB firmware available\n");
22980aa77f6cSMauro Carvalho Chehab 
22990aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < MAX_CHANNELS; j++) {
23000aa77f6cSMauro Carvalho Chehab 		struct s2255_channel *channel = &dev->channel[j];
23010aa77f6cSMauro Carvalho Chehab 		channel->b_acquire = 0;
23020aa77f6cSMauro Carvalho Chehab 		channel->mode = mode_def;
23030aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 && j > 1)
23040aa77f6cSMauro Carvalho Chehab 			channel->mode.color |= (1 << 16);
23050aa77f6cSMauro Carvalho Chehab 		channel->jc.quality = S2255_DEF_JPEG_QUAL;
23060aa77f6cSMauro Carvalho Chehab 		channel->width = LINE_SZ_4CIFS_NTSC;
23070aa77f6cSMauro Carvalho Chehab 		channel->height = NUM_LINES_4CIFS_NTSC * 2;
23080aa77f6cSMauro Carvalho Chehab 		channel->fmt = &formats[0];
23090aa77f6cSMauro Carvalho Chehab 		channel->mode.restart = 1;
23100aa77f6cSMauro Carvalho Chehab 		channel->req_image_size = get_transfer_size(&mode_def);
23110aa77f6cSMauro Carvalho Chehab 		channel->frame_count = 0;
23120aa77f6cSMauro Carvalho Chehab 		/* create the system buffers */
23130aa77f6cSMauro Carvalho Chehab 		s2255_create_sys_buffers(channel);
23140aa77f6cSMauro Carvalho Chehab 	}
23150aa77f6cSMauro Carvalho Chehab 	/* start read pipe */
23160aa77f6cSMauro Carvalho Chehab 	s2255_start_readpipe(dev);
23170aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s: success\n", __func__);
23180aa77f6cSMauro Carvalho Chehab 	return 0;
23190aa77f6cSMauro Carvalho Chehab }
23200aa77f6cSMauro Carvalho Chehab 
23210aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev)
23220aa77f6cSMauro Carvalho Chehab {
23230aa77f6cSMauro Carvalho Chehab 	u32 i;
23240aa77f6cSMauro Carvalho Chehab 	dprintk(1, "%s: dev: %p", __func__,  dev);
23250aa77f6cSMauro Carvalho Chehab 
23260aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
23270aa77f6cSMauro Carvalho Chehab 		if (dev->channel[i].b_acquire)
23280aa77f6cSMauro Carvalho Chehab 			s2255_stop_acquire(&dev->channel[i]);
23290aa77f6cSMauro Carvalho Chehab 	}
23300aa77f6cSMauro Carvalho Chehab 	s2255_stop_readpipe(dev);
23310aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++)
23320aa77f6cSMauro Carvalho Chehab 		s2255_release_sys_buffers(&dev->channel[i]);
23330aa77f6cSMauro Carvalho Chehab 	/* release transfer buffer */
23340aa77f6cSMauro Carvalho Chehab 	kfree(dev->pipe.transfer_buffer);
23350aa77f6cSMauro Carvalho Chehab 	return 0;
23360aa77f6cSMauro Carvalho Chehab }
23370aa77f6cSMauro Carvalho Chehab 
23380aa77f6cSMauro Carvalho Chehab static void read_pipe_completion(struct urb *purb)
23390aa77f6cSMauro Carvalho Chehab {
23400aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info;
23410aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev;
23420aa77f6cSMauro Carvalho Chehab 	int status;
23430aa77f6cSMauro Carvalho Chehab 	int pipe;
23440aa77f6cSMauro Carvalho Chehab 	pipe_info = purb->context;
23450aa77f6cSMauro Carvalho Chehab 	dprintk(100, "%s: urb:%p, status %d\n", __func__, purb,
23460aa77f6cSMauro Carvalho Chehab 		purb->status);
23470aa77f6cSMauro Carvalho Chehab 	if (pipe_info == NULL) {
23480aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
23490aa77f6cSMauro Carvalho Chehab 		return;
23500aa77f6cSMauro Carvalho Chehab 	}
23510aa77f6cSMauro Carvalho Chehab 
23520aa77f6cSMauro Carvalho Chehab 	dev = pipe_info->dev;
23530aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
23540aa77f6cSMauro Carvalho Chehab 		dev_err(&purb->dev->dev, "no context!\n");
23550aa77f6cSMauro Carvalho Chehab 		return;
23560aa77f6cSMauro Carvalho Chehab 	}
23570aa77f6cSMauro Carvalho Chehab 	status = purb->status;
23580aa77f6cSMauro Carvalho Chehab 	/* if shutting down, do not resubmit, exit immediately */
23590aa77f6cSMauro Carvalho Chehab 	if (status == -ESHUTDOWN) {
23600aa77f6cSMauro Carvalho Chehab 		dprintk(2, "%s: err shutdown\n", __func__);
23610aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
23620aa77f6cSMauro Carvalho Chehab 		return;
23630aa77f6cSMauro Carvalho Chehab 	}
23640aa77f6cSMauro Carvalho Chehab 
23650aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state == 0) {
23660aa77f6cSMauro Carvalho Chehab 		dprintk(2, "%s: exiting USB pipe", __func__);
23670aa77f6cSMauro Carvalho Chehab 		return;
23680aa77f6cSMauro Carvalho Chehab 	}
23690aa77f6cSMauro Carvalho Chehab 
23700aa77f6cSMauro Carvalho Chehab 	if (status == 0)
23710aa77f6cSMauro Carvalho Chehab 		s2255_read_video_callback(dev, pipe_info);
23720aa77f6cSMauro Carvalho Chehab 	else {
23730aa77f6cSMauro Carvalho Chehab 		pipe_info->err_count++;
23740aa77f6cSMauro Carvalho Chehab 		dprintk(1, "%s: failed URB %d\n", __func__, status);
23750aa77f6cSMauro Carvalho Chehab 	}
23760aa77f6cSMauro Carvalho Chehab 
23770aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
23780aa77f6cSMauro Carvalho Chehab 	/* reuse urb */
23790aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
23800aa77f6cSMauro Carvalho Chehab 			  pipe,
23810aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
23820aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
23830aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
23840aa77f6cSMauro Carvalho Chehab 
23850aa77f6cSMauro Carvalho Chehab 	if (pipe_info->state != 0) {
23860aa77f6cSMauro Carvalho Chehab 		if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) {
23870aa77f6cSMauro Carvalho Chehab 			dev_err(&dev->udev->dev, "error submitting urb\n");
23880aa77f6cSMauro Carvalho Chehab 		}
23890aa77f6cSMauro Carvalho Chehab 	} else {
23900aa77f6cSMauro Carvalho Chehab 		dprintk(2, "%s :complete state 0\n", __func__);
23910aa77f6cSMauro Carvalho Chehab 	}
23920aa77f6cSMauro Carvalho Chehab 	return;
23930aa77f6cSMauro Carvalho Chehab }
23940aa77f6cSMauro Carvalho Chehab 
23950aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev)
23960aa77f6cSMauro Carvalho Chehab {
23970aa77f6cSMauro Carvalho Chehab 	int pipe;
23980aa77f6cSMauro Carvalho Chehab 	int retval;
23990aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe_info = &dev->pipe;
24000aa77f6cSMauro Carvalho Chehab 	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
24010aa77f6cSMauro Carvalho Chehab 	dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint);
24020aa77f6cSMauro Carvalho Chehab 	pipe_info->state = 1;
24030aa77f6cSMauro Carvalho Chehab 	pipe_info->err_count = 0;
24040aa77f6cSMauro Carvalho Chehab 	pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
24050aa77f6cSMauro Carvalho Chehab 	if (!pipe_info->stream_urb) {
24060aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev,
24070aa77f6cSMauro Carvalho Chehab 			"ReadStream: Unable to alloc URB\n");
24080aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
24090aa77f6cSMauro Carvalho Chehab 	}
24100aa77f6cSMauro Carvalho Chehab 	/* transfer buffer allocated in board_init */
24110aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
24120aa77f6cSMauro Carvalho Chehab 			  pipe,
24130aa77f6cSMauro Carvalho Chehab 			  pipe_info->transfer_buffer,
24140aa77f6cSMauro Carvalho Chehab 			  pipe_info->cur_transfer_size,
24150aa77f6cSMauro Carvalho Chehab 			  read_pipe_completion, pipe_info);
24160aa77f6cSMauro Carvalho Chehab 	retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
24170aa77f6cSMauro Carvalho Chehab 	if (retval) {
24180aa77f6cSMauro Carvalho Chehab 		printk(KERN_ERR "s2255: start read pipe failed\n");
24190aa77f6cSMauro Carvalho Chehab 		return retval;
24200aa77f6cSMauro Carvalho Chehab 	}
24210aa77f6cSMauro Carvalho Chehab 	return 0;
24220aa77f6cSMauro Carvalho Chehab }
24230aa77f6cSMauro Carvalho Chehab 
24240aa77f6cSMauro Carvalho Chehab /* starts acquisition process */
24250aa77f6cSMauro Carvalho Chehab static int s2255_start_acquire(struct s2255_channel *channel)
24260aa77f6cSMauro Carvalho Chehab {
24270aa77f6cSMauro Carvalho Chehab 	unsigned char *buffer;
24280aa77f6cSMauro Carvalho Chehab 	int res;
24290aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
24300aa77f6cSMauro Carvalho Chehab 	int j;
24310aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
24320aa77f6cSMauro Carvalho Chehab 	chn_rev = G_chnmap[channel->idx];
24330aa77f6cSMauro Carvalho Chehab 	buffer = kzalloc(512, GFP_KERNEL);
24340aa77f6cSMauro Carvalho Chehab 	if (buffer == NULL) {
24350aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "out of mem\n");
24360aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
24370aa77f6cSMauro Carvalho Chehab 	}
24380aa77f6cSMauro Carvalho Chehab 
24390aa77f6cSMauro Carvalho Chehab 	channel->last_frame = -1;
24400aa77f6cSMauro Carvalho Chehab 	channel->bad_payload = 0;
24410aa77f6cSMauro Carvalho Chehab 	channel->cur_frame = 0;
24420aa77f6cSMauro Carvalho Chehab 	for (j = 0; j < SYS_FRAMES; j++) {
24430aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[j].ulState = 0;
24440aa77f6cSMauro Carvalho Chehab 		channel->buffer.frame[j].cur_size = 0;
24450aa77f6cSMauro Carvalho Chehab 	}
24460aa77f6cSMauro Carvalho Chehab 
24470aa77f6cSMauro Carvalho Chehab 	/* send the start command */
24480aa77f6cSMauro Carvalho Chehab 	*(__le32 *) buffer = IN_DATA_TOKEN;
24490aa77f6cSMauro Carvalho Chehab 	*((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
24500aa77f6cSMauro Carvalho Chehab 	*((__le32 *) buffer + 2) = CMD_START;
24510aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
24520aa77f6cSMauro Carvalho Chehab 	if (res != 0)
24530aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_START error\n");
24540aa77f6cSMauro Carvalho Chehab 
24550aa77f6cSMauro Carvalho Chehab 	dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res);
24560aa77f6cSMauro Carvalho Chehab 	kfree(buffer);
24570aa77f6cSMauro Carvalho Chehab 	return 0;
24580aa77f6cSMauro Carvalho Chehab }
24590aa77f6cSMauro Carvalho Chehab 
24600aa77f6cSMauro Carvalho Chehab static int s2255_stop_acquire(struct s2255_channel *channel)
24610aa77f6cSMauro Carvalho Chehab {
24620aa77f6cSMauro Carvalho Chehab 	unsigned char *buffer;
24630aa77f6cSMauro Carvalho Chehab 	int res;
24640aa77f6cSMauro Carvalho Chehab 	unsigned long chn_rev;
24650aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
24660aa77f6cSMauro Carvalho Chehab 	chn_rev = G_chnmap[channel->idx];
24670aa77f6cSMauro Carvalho Chehab 	buffer = kzalloc(512, GFP_KERNEL);
24680aa77f6cSMauro Carvalho Chehab 	if (buffer == NULL) {
24690aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "out of mem\n");
24700aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
24710aa77f6cSMauro Carvalho Chehab 	}
24720aa77f6cSMauro Carvalho Chehab 	/* send the stop command */
24730aa77f6cSMauro Carvalho Chehab 	*(__le32 *) buffer = IN_DATA_TOKEN;
24740aa77f6cSMauro Carvalho Chehab 	*((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
24750aa77f6cSMauro Carvalho Chehab 	*((__le32 *) buffer + 2) = CMD_STOP;
24760aa77f6cSMauro Carvalho Chehab 	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
24770aa77f6cSMauro Carvalho Chehab 	if (res != 0)
24780aa77f6cSMauro Carvalho Chehab 		dev_err(&dev->udev->dev, "CMD_STOP error\n");
24790aa77f6cSMauro Carvalho Chehab 	kfree(buffer);
24800aa77f6cSMauro Carvalho Chehab 	channel->b_acquire = 0;
24810aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res);
24820aa77f6cSMauro Carvalho Chehab 	return res;
24830aa77f6cSMauro Carvalho Chehab }
24840aa77f6cSMauro Carvalho Chehab 
24850aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev)
24860aa77f6cSMauro Carvalho Chehab {
24870aa77f6cSMauro Carvalho Chehab 	struct s2255_pipeinfo *pipe = &dev->pipe;
24880aa77f6cSMauro Carvalho Chehab 
24890aa77f6cSMauro Carvalho Chehab 	pipe->state = 0;
24900aa77f6cSMauro Carvalho Chehab 	if (pipe->stream_urb) {
24910aa77f6cSMauro Carvalho Chehab 		/* cancel urb */
24920aa77f6cSMauro Carvalho Chehab 		usb_kill_urb(pipe->stream_urb);
24930aa77f6cSMauro Carvalho Chehab 		usb_free_urb(pipe->stream_urb);
24940aa77f6cSMauro Carvalho Chehab 		pipe->stream_urb = NULL;
24950aa77f6cSMauro Carvalho Chehab 	}
24960aa77f6cSMauro Carvalho Chehab 	dprintk(4, "%s", __func__);
24970aa77f6cSMauro Carvalho Chehab 	return;
24980aa77f6cSMauro Carvalho Chehab }
24990aa77f6cSMauro Carvalho Chehab 
25000aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset)
25010aa77f6cSMauro Carvalho Chehab {
25020aa77f6cSMauro Carvalho Chehab 	if (reset)
25030aa77f6cSMauro Carvalho Chehab 		s2255_reset_dsppower(dev);
25040aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_size = dev->fw_data->fw->size;
25050aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
25060aa77f6cSMauro Carvalho Chehab 	memcpy(dev->fw_data->pfw_data,
25070aa77f6cSMauro Carvalho Chehab 	       dev->fw_data->fw->data, CHUNK_SIZE);
25080aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_loaded = CHUNK_SIZE;
25090aa77f6cSMauro Carvalho Chehab 	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
25100aa77f6cSMauro Carvalho Chehab 			  usb_sndbulkpipe(dev->udev, 2),
25110aa77f6cSMauro Carvalho Chehab 			  dev->fw_data->pfw_data,
25120aa77f6cSMauro Carvalho Chehab 			  CHUNK_SIZE, s2255_fwchunk_complete,
25130aa77f6cSMauro Carvalho Chehab 			  dev->fw_data);
25140aa77f6cSMauro Carvalho Chehab 	mod_timer(&dev->timer, jiffies + HZ);
25150aa77f6cSMauro Carvalho Chehab }
25160aa77f6cSMauro Carvalho Chehab 
25170aa77f6cSMauro Carvalho Chehab /* standard usb probe function */
25180aa77f6cSMauro Carvalho Chehab static int s2255_probe(struct usb_interface *interface,
25190aa77f6cSMauro Carvalho Chehab 		       const struct usb_device_id *id)
25200aa77f6cSMauro Carvalho Chehab {
25210aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = NULL;
25220aa77f6cSMauro Carvalho Chehab 	struct usb_host_interface *iface_desc;
25230aa77f6cSMauro Carvalho Chehab 	struct usb_endpoint_descriptor *endpoint;
25240aa77f6cSMauro Carvalho Chehab 	int i;
25250aa77f6cSMauro Carvalho Chehab 	int retval = -ENOMEM;
25260aa77f6cSMauro Carvalho Chehab 	__le32 *pdata;
25270aa77f6cSMauro Carvalho Chehab 	int fw_size;
25280aa77f6cSMauro Carvalho Chehab 	dprintk(2, "%s\n", __func__);
25290aa77f6cSMauro Carvalho Chehab 	/* allocate memory for our device state and initialize it to zero */
25300aa77f6cSMauro Carvalho Chehab 	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
25310aa77f6cSMauro Carvalho Chehab 	if (dev == NULL) {
25320aa77f6cSMauro Carvalho Chehab 		s2255_dev_err(&interface->dev, "out of memory\n");
25330aa77f6cSMauro Carvalho Chehab 		return -ENOMEM;
25340aa77f6cSMauro Carvalho Chehab 	}
25350aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->num_channels, 0);
25360aa77f6cSMauro Carvalho Chehab 	dev->pid = id->idProduct;
25370aa77f6cSMauro Carvalho Chehab 	dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
25380aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data)
25390aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA1;
25400aa77f6cSMauro Carvalho Chehab 	mutex_init(&dev->lock);
25410aa77f6cSMauro Carvalho Chehab 	/* grab usb_device and save it */
25420aa77f6cSMauro Carvalho Chehab 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
25430aa77f6cSMauro Carvalho Chehab 	if (dev->udev == NULL) {
25440aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "null usb device\n");
25450aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
25460aa77f6cSMauro Carvalho Chehab 		goto errorUDEV;
25470aa77f6cSMauro Carvalho Chehab 	}
25480aa77f6cSMauro Carvalho Chehab 	dprintk(1, "dev: %p, udev %p interface %p\n", dev,
25490aa77f6cSMauro Carvalho Chehab 		dev->udev, interface);
25500aa77f6cSMauro Carvalho Chehab 	dev->interface = interface;
25510aa77f6cSMauro Carvalho Chehab 	/* set up the endpoint information  */
25520aa77f6cSMauro Carvalho Chehab 	iface_desc = interface->cur_altsetting;
25530aa77f6cSMauro Carvalho Chehab 	dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
25540aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
25550aa77f6cSMauro Carvalho Chehab 		endpoint = &iface_desc->endpoint[i].desc;
25560aa77f6cSMauro Carvalho Chehab 		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
25570aa77f6cSMauro Carvalho Chehab 			/* we found the bulk in endpoint */
25580aa77f6cSMauro Carvalho Chehab 			dev->read_endpoint = endpoint->bEndpointAddress;
25590aa77f6cSMauro Carvalho Chehab 		}
25600aa77f6cSMauro Carvalho Chehab 	}
25610aa77f6cSMauro Carvalho Chehab 
25620aa77f6cSMauro Carvalho Chehab 	if (!dev->read_endpoint) {
25630aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
25640aa77f6cSMauro Carvalho Chehab 		goto errorEP;
25650aa77f6cSMauro Carvalho Chehab 	}
25660aa77f6cSMauro Carvalho Chehab 	init_timer(&dev->timer);
25670aa77f6cSMauro Carvalho Chehab 	dev->timer.function = s2255_timer;
25680aa77f6cSMauro Carvalho Chehab 	dev->timer.data = (unsigned long)dev->fw_data;
25690aa77f6cSMauro Carvalho Chehab 	init_waitqueue_head(&dev->fw_data->wait_fw);
25700aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
25710aa77f6cSMauro Carvalho Chehab 		struct s2255_channel *channel = &dev->channel[i];
25720aa77f6cSMauro Carvalho Chehab 		dev->channel[i].idx = i;
25730aa77f6cSMauro Carvalho Chehab 		init_waitqueue_head(&channel->wait_setmode);
25740aa77f6cSMauro Carvalho Chehab 		init_waitqueue_head(&channel->wait_vidstatus);
25750aa77f6cSMauro Carvalho Chehab 	}
25760aa77f6cSMauro Carvalho Chehab 
25770aa77f6cSMauro Carvalho Chehab 	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
25780aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data->fw_urb) {
25790aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "out of memory!\n");
25800aa77f6cSMauro Carvalho Chehab 		goto errorFWURB;
25810aa77f6cSMauro Carvalho Chehab 	}
25820aa77f6cSMauro Carvalho Chehab 
25830aa77f6cSMauro Carvalho Chehab 	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
25840aa77f6cSMauro Carvalho Chehab 	if (!dev->fw_data->pfw_data) {
25850aa77f6cSMauro Carvalho Chehab 		dev_err(&interface->dev, "out of memory!\n");
25860aa77f6cSMauro Carvalho Chehab 		goto errorFWDATA2;
25870aa77f6cSMauro Carvalho Chehab 	}
25880aa77f6cSMauro Carvalho Chehab 	/* load the first chunk */
25890aa77f6cSMauro Carvalho Chehab 	if (request_firmware(&dev->fw_data->fw,
25900aa77f6cSMauro Carvalho Chehab 			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
25910aa77f6cSMauro Carvalho Chehab 		printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
25920aa77f6cSMauro Carvalho Chehab 		goto errorREQFW;
25930aa77f6cSMauro Carvalho Chehab 	}
25940aa77f6cSMauro Carvalho Chehab 	/* check the firmware is valid */
25950aa77f6cSMauro Carvalho Chehab 	fw_size = dev->fw_data->fw->size;
25960aa77f6cSMauro Carvalho Chehab 	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
25970aa77f6cSMauro Carvalho Chehab 
25980aa77f6cSMauro Carvalho Chehab 	if (*pdata != S2255_FW_MARKER) {
25990aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "Firmware invalid.\n");
26000aa77f6cSMauro Carvalho Chehab 		retval = -ENODEV;
26010aa77f6cSMauro Carvalho Chehab 		goto errorFWMARKER;
26020aa77f6cSMauro Carvalho Chehab 	} else {
26030aa77f6cSMauro Carvalho Chehab 		/* make sure firmware is the latest */
26040aa77f6cSMauro Carvalho Chehab 		__le32 *pRel;
26050aa77f6cSMauro Carvalho Chehab 		pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
26060aa77f6cSMauro Carvalho Chehab 		printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
26070aa77f6cSMauro Carvalho Chehab 		dev->dsp_fw_ver = le32_to_cpu(*pRel);
26080aa77f6cSMauro Carvalho Chehab 		if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
26090aa77f6cSMauro Carvalho Chehab 			printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
26100aa77f6cSMauro Carvalho Chehab 		if (dev->pid == 0x2257 &&
26110aa77f6cSMauro Carvalho Chehab 				dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
26120aa77f6cSMauro Carvalho Chehab 			printk(KERN_WARNING "s2255: 2257 requires firmware %d"
26130aa77f6cSMauro Carvalho Chehab 			       " or above.\n", S2255_MIN_DSP_COLORFILTER);
26140aa77f6cSMauro Carvalho Chehab 	}
26150aa77f6cSMauro Carvalho Chehab 	usb_reset_device(dev->udev);
26160aa77f6cSMauro Carvalho Chehab 	/* load 2255 board specific */
26170aa77f6cSMauro Carvalho Chehab 	retval = s2255_board_init(dev);
26180aa77f6cSMauro Carvalho Chehab 	if (retval)
26190aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
26200aa77f6cSMauro Carvalho Chehab 	spin_lock_init(&dev->slock);
26210aa77f6cSMauro Carvalho Chehab 	s2255_fwload_start(dev, 0);
26220aa77f6cSMauro Carvalho Chehab 	/* loads v4l specific */
26230aa77f6cSMauro Carvalho Chehab 	retval = s2255_probe_v4l(dev);
26240aa77f6cSMauro Carvalho Chehab 	if (retval)
26250aa77f6cSMauro Carvalho Chehab 		goto errorBOARDINIT;
26260aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "Sensoray 2255 detected\n");
26270aa77f6cSMauro Carvalho Chehab 	return 0;
26280aa77f6cSMauro Carvalho Chehab errorBOARDINIT:
26290aa77f6cSMauro Carvalho Chehab 	s2255_board_shutdown(dev);
26300aa77f6cSMauro Carvalho Chehab errorFWMARKER:
26310aa77f6cSMauro Carvalho Chehab 	release_firmware(dev->fw_data->fw);
26320aa77f6cSMauro Carvalho Chehab errorREQFW:
26330aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data->pfw_data);
26340aa77f6cSMauro Carvalho Chehab errorFWDATA2:
26350aa77f6cSMauro Carvalho Chehab 	usb_free_urb(dev->fw_data->fw_urb);
26360aa77f6cSMauro Carvalho Chehab errorFWURB:
26370aa77f6cSMauro Carvalho Chehab 	del_timer(&dev->timer);
26380aa77f6cSMauro Carvalho Chehab errorEP:
26390aa77f6cSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
26400aa77f6cSMauro Carvalho Chehab errorUDEV:
26410aa77f6cSMauro Carvalho Chehab 	kfree(dev->fw_data);
26420aa77f6cSMauro Carvalho Chehab 	mutex_destroy(&dev->lock);
26430aa77f6cSMauro Carvalho Chehab errorFWDATA1:
26440aa77f6cSMauro Carvalho Chehab 	kfree(dev);
26450aa77f6cSMauro Carvalho Chehab 	printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval);
26460aa77f6cSMauro Carvalho Chehab 	return retval;
26470aa77f6cSMauro Carvalho Chehab }
26480aa77f6cSMauro Carvalho Chehab 
26490aa77f6cSMauro Carvalho Chehab /* disconnect routine. when board is removed physically or with rmmod */
26500aa77f6cSMauro Carvalho Chehab static void s2255_disconnect(struct usb_interface *interface)
26510aa77f6cSMauro Carvalho Chehab {
26520aa77f6cSMauro Carvalho Chehab 	struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
26530aa77f6cSMauro Carvalho Chehab 	int i;
26540aa77f6cSMauro Carvalho Chehab 	int channels = atomic_read(&dev->num_channels);
26550aa77f6cSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
26560aa77f6cSMauro Carvalho Chehab 	v4l2_device_disconnect(&dev->v4l2_dev);
26570aa77f6cSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
26580aa77f6cSMauro Carvalho Chehab 	/*see comments in the uvc_driver.c usb disconnect function */
26590aa77f6cSMauro Carvalho Chehab 	atomic_inc(&dev->num_channels);
26600aa77f6cSMauro Carvalho Chehab 	/* unregister each video device. */
26610aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < channels; i++)
26620aa77f6cSMauro Carvalho Chehab 		video_unregister_device(&dev->channel[i].vdev);
26630aa77f6cSMauro Carvalho Chehab 	/* wake up any of our timers */
26640aa77f6cSMauro Carvalho Chehab 	atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
26650aa77f6cSMauro Carvalho Chehab 	wake_up(&dev->fw_data->wait_fw);
26660aa77f6cSMauro Carvalho Chehab 	for (i = 0; i < MAX_CHANNELS; i++) {
26670aa77f6cSMauro Carvalho Chehab 		dev->channel[i].setmode_ready = 1;
26680aa77f6cSMauro Carvalho Chehab 		wake_up(&dev->channel[i].wait_setmode);
26690aa77f6cSMauro Carvalho Chehab 		dev->channel[i].vidstatus_ready = 1;
26700aa77f6cSMauro Carvalho Chehab 		wake_up(&dev->channel[i].wait_vidstatus);
26710aa77f6cSMauro Carvalho Chehab 	}
26720aa77f6cSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->num_channels))
26730aa77f6cSMauro Carvalho Chehab 		s2255_destroy(dev);
26740aa77f6cSMauro Carvalho Chehab 	dev_info(&interface->dev, "%s\n", __func__);
26750aa77f6cSMauro Carvalho Chehab }
26760aa77f6cSMauro Carvalho Chehab 
26770aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver = {
26780aa77f6cSMauro Carvalho Chehab 	.name = S2255_DRIVER_NAME,
26790aa77f6cSMauro Carvalho Chehab 	.probe = s2255_probe,
26800aa77f6cSMauro Carvalho Chehab 	.disconnect = s2255_disconnect,
26810aa77f6cSMauro Carvalho Chehab 	.id_table = s2255_table,
26820aa77f6cSMauro Carvalho Chehab };
26830aa77f6cSMauro Carvalho Chehab 
26840aa77f6cSMauro Carvalho Chehab module_usb_driver(s2255_driver);
26850aa77f6cSMauro Carvalho Chehab 
26860aa77f6cSMauro Carvalho Chehab MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
26870aa77f6cSMauro Carvalho Chehab MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
26880aa77f6cSMauro Carvalho Chehab MODULE_LICENSE("GPL");
26890aa77f6cSMauro Carvalho Chehab MODULE_VERSION(S2255_VERSION);
26900aa77f6cSMauro Carvalho Chehab MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
2691