18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  s2255drv.c - a driver for the Sensoray 2255 USB video capture device
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   Copyright (C) 2007-2014 by Sensoray Company Inc.
68c2ecf20Sopenharmony_ci *                              Dean Anderson
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Some video buffer code based on vivi driver:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Sensoray 2255 device supports 4 simultaneous channels.
118c2ecf20Sopenharmony_ci * The channels are not "crossbar" inputs, they are physically
128c2ecf20Sopenharmony_ci * attached to separate video decoders.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Because of USB2.0 bandwidth limitations. There is only a
158c2ecf20Sopenharmony_ci * certain amount of data which may be transferred at one time.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Example maximum bandwidth utilization:
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * -full size, color mode YUYV or YUV422P: 2 channels at once
208c2ecf20Sopenharmony_ci * -full or half size Grey scale: all 4 channels at once
218c2ecf20Sopenharmony_ci * -half size, color mode YUYV or YUV422P: all 4 channels at once
228c2ecf20Sopenharmony_ci * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
238c2ecf20Sopenharmony_ci *  at once.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/firmware.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/mutex.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
328c2ecf20Sopenharmony_ci#include <linux/mm.h>
338c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
348c2ecf20Sopenharmony_ci#include <linux/usb.h>
358c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h>
368c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h>
378c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
388c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
398c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
408c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
418c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define S2255_VERSION		"1.25.1"
448c2ecf20Sopenharmony_ci#define FIRMWARE_FILE_NAME "f2255usb.bin"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* default JPEG quality */
478c2ecf20Sopenharmony_ci#define S2255_DEF_JPEG_QUAL     50
488c2ecf20Sopenharmony_ci/* vendor request in */
498c2ecf20Sopenharmony_ci#define S2255_VR_IN		0
508c2ecf20Sopenharmony_ci/* vendor request out */
518c2ecf20Sopenharmony_ci#define S2255_VR_OUT		1
528c2ecf20Sopenharmony_ci/* firmware query */
538c2ecf20Sopenharmony_ci#define S2255_VR_FW		0x30
548c2ecf20Sopenharmony_ci/* USB endpoint number for configuring the device */
558c2ecf20Sopenharmony_ci#define S2255_CONFIG_EP         2
568c2ecf20Sopenharmony_ci/* maximum time for DSP to start responding after last FW word loaded(ms) */
578c2ecf20Sopenharmony_ci#define S2255_DSP_BOOTTIME      800
588c2ecf20Sopenharmony_ci/* maximum time to wait for firmware to load (ms) */
598c2ecf20Sopenharmony_ci#define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
608c2ecf20Sopenharmony_ci#define S2255_MIN_BUFS          2
618c2ecf20Sopenharmony_ci#define S2255_SETMODE_TIMEOUT   500
628c2ecf20Sopenharmony_ci#define S2255_VIDSTATUS_TIMEOUT 350
638c2ecf20Sopenharmony_ci#define S2255_MARKER_FRAME	cpu_to_le32(0x2255DA4AL)
648c2ecf20Sopenharmony_ci#define S2255_MARKER_RESPONSE	cpu_to_le32(0x2255ACACL)
658c2ecf20Sopenharmony_ci#define S2255_RESPONSE_SETMODE  cpu_to_le32(0x01)
668c2ecf20Sopenharmony_ci#define S2255_RESPONSE_FW       cpu_to_le32(0x10)
678c2ecf20Sopenharmony_ci#define S2255_RESPONSE_STATUS   cpu_to_le32(0x20)
688c2ecf20Sopenharmony_ci#define S2255_USB_XFER_SIZE	(16 * 1024)
698c2ecf20Sopenharmony_ci#define MAX_CHANNELS		4
708c2ecf20Sopenharmony_ci#define SYS_FRAMES		4
718c2ecf20Sopenharmony_ci/* maximum size is PAL full size plus room for the marker header(s) */
728c2ecf20Sopenharmony_ci#define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
738c2ecf20Sopenharmony_ci#define DEF_USB_BLOCK		S2255_USB_XFER_SIZE
748c2ecf20Sopenharmony_ci#define LINE_SZ_4CIFS_NTSC	640
758c2ecf20Sopenharmony_ci#define LINE_SZ_2CIFS_NTSC	640
768c2ecf20Sopenharmony_ci#define LINE_SZ_1CIFS_NTSC	320
778c2ecf20Sopenharmony_ci#define LINE_SZ_4CIFS_PAL	704
788c2ecf20Sopenharmony_ci#define LINE_SZ_2CIFS_PAL	704
798c2ecf20Sopenharmony_ci#define LINE_SZ_1CIFS_PAL	352
808c2ecf20Sopenharmony_ci#define NUM_LINES_4CIFS_NTSC	240
818c2ecf20Sopenharmony_ci#define NUM_LINES_2CIFS_NTSC	240
828c2ecf20Sopenharmony_ci#define NUM_LINES_1CIFS_NTSC	240
838c2ecf20Sopenharmony_ci#define NUM_LINES_4CIFS_PAL	288
848c2ecf20Sopenharmony_ci#define NUM_LINES_2CIFS_PAL	288
858c2ecf20Sopenharmony_ci#define NUM_LINES_1CIFS_PAL	288
868c2ecf20Sopenharmony_ci#define LINE_SZ_DEF		640
878c2ecf20Sopenharmony_ci#define NUM_LINES_DEF		240
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* predefined settings */
918c2ecf20Sopenharmony_ci#define FORMAT_NTSC	1
928c2ecf20Sopenharmony_ci#define FORMAT_PAL	2
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
958c2ecf20Sopenharmony_ci#define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
968c2ecf20Sopenharmony_ci#define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
978c2ecf20Sopenharmony_ci/* SCALE_4CIFSI is the 2 fields interpolated into one */
988c2ecf20Sopenharmony_ci#define SCALE_4CIFSI	4	/* 640x480(NTSC) or 704x576(PAL) high quality */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define COLOR_YUVPL	1	/* YUV planar */
1018c2ecf20Sopenharmony_ci#define COLOR_YUVPK	2	/* YUV packed */
1028c2ecf20Sopenharmony_ci#define COLOR_Y8	4	/* monochrome */
1038c2ecf20Sopenharmony_ci#define COLOR_JPG       5       /* JPEG */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#define MASK_COLOR       0x000000ff
1068c2ecf20Sopenharmony_ci#define MASK_JPG_QUALITY 0x0000ff00
1078c2ecf20Sopenharmony_ci#define MASK_INPUT_TYPE  0x000f0000
1088c2ecf20Sopenharmony_ci/* frame decimation. */
1098c2ecf20Sopenharmony_ci#define FDEC_1		1	/* capture every frame. default */
1108c2ecf20Sopenharmony_ci#define FDEC_2		2	/* capture every 2nd frame */
1118c2ecf20Sopenharmony_ci#define FDEC_3		3	/* capture every 3rd frame */
1128c2ecf20Sopenharmony_ci#define FDEC_5		5	/* capture every 5th frame */
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/*-------------------------------------------------------
1158c2ecf20Sopenharmony_ci * Default mode parameters.
1168c2ecf20Sopenharmony_ci *-------------------------------------------------------*/
1178c2ecf20Sopenharmony_ci#define DEF_SCALE	SCALE_4CIFS
1188c2ecf20Sopenharmony_ci#define DEF_COLOR	COLOR_YUVPL
1198c2ecf20Sopenharmony_ci#define DEF_FDEC	FDEC_1
1208c2ecf20Sopenharmony_ci#define DEF_BRIGHT	0
1218c2ecf20Sopenharmony_ci#define DEF_CONTRAST	0x5c
1228c2ecf20Sopenharmony_ci#define DEF_SATURATION	0x80
1238c2ecf20Sopenharmony_ci#define DEF_HUE		0
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* usb config commands */
1268c2ecf20Sopenharmony_ci#define IN_DATA_TOKEN	cpu_to_le32(0x2255c0de)
1278c2ecf20Sopenharmony_ci#define CMD_2255	0xc2255000
1288c2ecf20Sopenharmony_ci#define CMD_SET_MODE	cpu_to_le32((CMD_2255 | 0x10))
1298c2ecf20Sopenharmony_ci#define CMD_START	cpu_to_le32((CMD_2255 | 0x20))
1308c2ecf20Sopenharmony_ci#define CMD_STOP	cpu_to_le32((CMD_2255 | 0x30))
1318c2ecf20Sopenharmony_ci#define CMD_STATUS	cpu_to_le32((CMD_2255 | 0x40))
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistruct s2255_mode {
1348c2ecf20Sopenharmony_ci	u32 format;	/* input video format (NTSC, PAL) */
1358c2ecf20Sopenharmony_ci	u32 scale;	/* output video scale */
1368c2ecf20Sopenharmony_ci	u32 color;	/* output video color format */
1378c2ecf20Sopenharmony_ci	u32 fdec;	/* frame decimation */
1388c2ecf20Sopenharmony_ci	u32 bright;	/* brightness */
1398c2ecf20Sopenharmony_ci	u32 contrast;	/* contrast */
1408c2ecf20Sopenharmony_ci	u32 saturation;	/* saturation */
1418c2ecf20Sopenharmony_ci	u32 hue;	/* hue (NTSC only)*/
1428c2ecf20Sopenharmony_ci	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
1438c2ecf20Sopenharmony_ci	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
1448c2ecf20Sopenharmony_ci	u32 restart;	/* if DSP requires restart */
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#define S2255_READ_IDLE		0
1498c2ecf20Sopenharmony_ci#define S2255_READ_FRAME	1
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* frame structure */
1528c2ecf20Sopenharmony_cistruct s2255_framei {
1538c2ecf20Sopenharmony_ci	unsigned long size;
1548c2ecf20Sopenharmony_ci	unsigned long ulState;	/* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
1558c2ecf20Sopenharmony_ci	void *lpvbits;		/* image data */
1568c2ecf20Sopenharmony_ci	unsigned long cur_size;	/* current data copied to it */
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* image buffer structure */
1608c2ecf20Sopenharmony_cistruct s2255_bufferi {
1618c2ecf20Sopenharmony_ci	unsigned long dwFrames;			/* number of frames in buffer */
1628c2ecf20Sopenharmony_ci	struct s2255_framei frame[SYS_FRAMES];	/* array of FRAME structures */
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci#define DEF_MODEI_NTSC_CONT	{FORMAT_NTSC, DEF_SCALE, DEF_COLOR,	\
1668c2ecf20Sopenharmony_ci			DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
1678c2ecf20Sopenharmony_ci			DEF_HUE, 0, DEF_USB_BLOCK, 0}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/* for firmware loading, fw_state */
1708c2ecf20Sopenharmony_ci#define S2255_FW_NOTLOADED	0
1718c2ecf20Sopenharmony_ci#define S2255_FW_LOADED_DSPWAIT	1
1728c2ecf20Sopenharmony_ci#define S2255_FW_SUCCESS	2
1738c2ecf20Sopenharmony_ci#define S2255_FW_FAILED		3
1748c2ecf20Sopenharmony_ci#define S2255_FW_DISCONNECTING  4
1758c2ecf20Sopenharmony_ci#define S2255_FW_MARKER		cpu_to_le32(0x22552f2f)
1768c2ecf20Sopenharmony_ci/* 2255 read states */
1778c2ecf20Sopenharmony_ci#define S2255_READ_IDLE         0
1788c2ecf20Sopenharmony_ci#define S2255_READ_FRAME        1
1798c2ecf20Sopenharmony_cistruct s2255_fw {
1808c2ecf20Sopenharmony_ci	int		      fw_loaded;
1818c2ecf20Sopenharmony_ci	int		      fw_size;
1828c2ecf20Sopenharmony_ci	struct urb	      *fw_urb;
1838c2ecf20Sopenharmony_ci	atomic_t	      fw_state;
1848c2ecf20Sopenharmony_ci	void		      *pfw_data;
1858c2ecf20Sopenharmony_ci	wait_queue_head_t     wait_fw;
1868c2ecf20Sopenharmony_ci	const struct firmware *fw;
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistruct s2255_pipeinfo {
1908c2ecf20Sopenharmony_ci	u32 max_transfer_size;
1918c2ecf20Sopenharmony_ci	u32 cur_transfer_size;
1928c2ecf20Sopenharmony_ci	u8 *transfer_buffer;
1938c2ecf20Sopenharmony_ci	u32 state;
1948c2ecf20Sopenharmony_ci	void *stream_urb;
1958c2ecf20Sopenharmony_ci	void *dev;	/* back pointer to s2255_dev struct*/
1968c2ecf20Sopenharmony_ci	u32 err_count;
1978c2ecf20Sopenharmony_ci	u32 idx;
1988c2ecf20Sopenharmony_ci};
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistruct s2255_fmt; /*forward declaration */
2018c2ecf20Sopenharmony_cistruct s2255_dev;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* 2255 video channel */
2048c2ecf20Sopenharmony_cistruct s2255_vc {
2058c2ecf20Sopenharmony_ci	struct s2255_dev        *dev;
2068c2ecf20Sopenharmony_ci	struct video_device	vdev;
2078c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
2088c2ecf20Sopenharmony_ci	struct v4l2_ctrl	*jpegqual_ctrl;
2098c2ecf20Sopenharmony_ci	int			resources;
2108c2ecf20Sopenharmony_ci	struct list_head        buf_list;
2118c2ecf20Sopenharmony_ci	struct s2255_bufferi	buffer;
2128c2ecf20Sopenharmony_ci	struct s2255_mode	mode;
2138c2ecf20Sopenharmony_ci	v4l2_std_id		std;
2148c2ecf20Sopenharmony_ci	/* jpeg compression */
2158c2ecf20Sopenharmony_ci	unsigned		jpegqual;
2168c2ecf20Sopenharmony_ci	/* capture parameters (for high quality mode full size) */
2178c2ecf20Sopenharmony_ci	struct v4l2_captureparm cap_parm;
2188c2ecf20Sopenharmony_ci	int			cur_frame;
2198c2ecf20Sopenharmony_ci	int			last_frame;
2208c2ecf20Sopenharmony_ci	/* allocated image size */
2218c2ecf20Sopenharmony_ci	unsigned long		req_image_size;
2228c2ecf20Sopenharmony_ci	/* received packet size */
2238c2ecf20Sopenharmony_ci	unsigned long		pkt_size;
2248c2ecf20Sopenharmony_ci	int			bad_payload;
2258c2ecf20Sopenharmony_ci	unsigned long		frame_count;
2268c2ecf20Sopenharmony_ci	/* if JPEG image */
2278c2ecf20Sopenharmony_ci	int                     jpg_size;
2288c2ecf20Sopenharmony_ci	/* if channel configured to default state */
2298c2ecf20Sopenharmony_ci	int                     configured;
2308c2ecf20Sopenharmony_ci	wait_queue_head_t       wait_setmode;
2318c2ecf20Sopenharmony_ci	int                     setmode_ready;
2328c2ecf20Sopenharmony_ci	/* video status items */
2338c2ecf20Sopenharmony_ci	int                     vidstatus;
2348c2ecf20Sopenharmony_ci	wait_queue_head_t       wait_vidstatus;
2358c2ecf20Sopenharmony_ci	int                     vidstatus_ready;
2368c2ecf20Sopenharmony_ci	unsigned int		width;
2378c2ecf20Sopenharmony_ci	unsigned int		height;
2388c2ecf20Sopenharmony_ci	enum v4l2_field         field;
2398c2ecf20Sopenharmony_ci	const struct s2255_fmt	*fmt;
2408c2ecf20Sopenharmony_ci	int idx; /* channel number on device, 0-3 */
2418c2ecf20Sopenharmony_ci	struct vb2_queue vb_vidq;
2428c2ecf20Sopenharmony_ci	struct mutex vb_lock; /* streaming lock */
2438c2ecf20Sopenharmony_ci	spinlock_t qlock;
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistruct s2255_dev {
2488c2ecf20Sopenharmony_ci	struct s2255_vc         vc[MAX_CHANNELS];
2498c2ecf20Sopenharmony_ci	struct v4l2_device      v4l2_dev;
2508c2ecf20Sopenharmony_ci	atomic_t                num_channels;
2518c2ecf20Sopenharmony_ci	int			frames;
2528c2ecf20Sopenharmony_ci	struct mutex		lock;	/* channels[].vdev.lock */
2538c2ecf20Sopenharmony_ci	struct mutex		cmdlock; /* protects cmdbuf */
2548c2ecf20Sopenharmony_ci	struct usb_device	*udev;
2558c2ecf20Sopenharmony_ci	struct usb_interface	*interface;
2568c2ecf20Sopenharmony_ci	u8			read_endpoint;
2578c2ecf20Sopenharmony_ci	struct timer_list	timer;
2588c2ecf20Sopenharmony_ci	struct s2255_fw	*fw_data;
2598c2ecf20Sopenharmony_ci	struct s2255_pipeinfo	pipe;
2608c2ecf20Sopenharmony_ci	u32			cc;	/* current channel */
2618c2ecf20Sopenharmony_ci	int			frame_ready;
2628c2ecf20Sopenharmony_ci	int                     chn_ready;
2638c2ecf20Sopenharmony_ci	/* dsp firmware version (f2255usb.bin) */
2648c2ecf20Sopenharmony_ci	int                     dsp_fw_ver;
2658c2ecf20Sopenharmony_ci	u16                     pid; /* product id */
2668c2ecf20Sopenharmony_ci#define S2255_CMDBUF_SIZE 512
2678c2ecf20Sopenharmony_ci	__le32                  *cmdbuf;
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistruct s2255_fmt {
2768c2ecf20Sopenharmony_ci	u32 fourcc;
2778c2ecf20Sopenharmony_ci	int depth;
2788c2ecf20Sopenharmony_ci};
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/* buffer for one video frame */
2818c2ecf20Sopenharmony_cistruct s2255_buffer {
2828c2ecf20Sopenharmony_ci	/* common v4l buffer stuff -- must be first */
2838c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer vb;
2848c2ecf20Sopenharmony_ci	struct list_head list;
2858c2ecf20Sopenharmony_ci};
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/* current cypress EEPROM firmware version */
2898c2ecf20Sopenharmony_ci#define S2255_CUR_USB_FWVER	((3 << 8) | 12)
2908c2ecf20Sopenharmony_ci/* current DSP FW version */
2918c2ecf20Sopenharmony_ci#define S2255_CUR_DSP_FWVER     10104
2928c2ecf20Sopenharmony_ci/* Need DSP version 5+ for video status feature */
2938c2ecf20Sopenharmony_ci#define S2255_MIN_DSP_STATUS      5
2948c2ecf20Sopenharmony_ci#define S2255_MIN_DSP_COLORFILTER 8
2958c2ecf20Sopenharmony_ci#define S2255_NORMS		(V4L2_STD_ALL)
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/* private V4L2 controls */
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci/*
3008c2ecf20Sopenharmony_ci * The following chart displays how COLORFILTER should be set
3018c2ecf20Sopenharmony_ci *  =========================================================
3028c2ecf20Sopenharmony_ci *  =     fourcc              =     COLORFILTER             =
3038c2ecf20Sopenharmony_ci *  =                         ===============================
3048c2ecf20Sopenharmony_ci *  =                         =   0             =    1      =
3058c2ecf20Sopenharmony_ci *  =========================================================
3068c2ecf20Sopenharmony_ci *  =  V4L2_PIX_FMT_GREY(Y8)  = monochrome from = monochrome=
3078c2ecf20Sopenharmony_ci *  =                         = s-video or      = composite =
3088c2ecf20Sopenharmony_ci *  =                         = B/W camera      = input     =
3098c2ecf20Sopenharmony_ci *  =========================================================
3108c2ecf20Sopenharmony_ci *  =    other                = color, svideo   = color,    =
3118c2ecf20Sopenharmony_ci *  =                         =                 = composite =
3128c2ecf20Sopenharmony_ci *  =========================================================
3138c2ecf20Sopenharmony_ci *
3148c2ecf20Sopenharmony_ci * Notes:
3158c2ecf20Sopenharmony_ci *   channels 0-3 on 2255 are composite
3168c2ecf20Sopenharmony_ci *   channels 0-1 on 2257 are composite, 2-3 are s-video
3178c2ecf20Sopenharmony_ci * If COLORFILTER is 0 with a composite color camera connected,
3188c2ecf20Sopenharmony_ci * the output will appear monochrome but hatching
3198c2ecf20Sopenharmony_ci * will occur.
3208c2ecf20Sopenharmony_ci * COLORFILTER is different from "color killer" and "color effects"
3218c2ecf20Sopenharmony_ci * for reasons above.
3228c2ecf20Sopenharmony_ci */
3238c2ecf20Sopenharmony_ci#define S2255_V4L2_YC_ON  1
3248c2ecf20Sopenharmony_ci#define S2255_V4L2_YC_OFF 0
3258c2ecf20Sopenharmony_ci#define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0)
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/* frame prefix size (sent once every frame) */
3288c2ecf20Sopenharmony_ci#define PREFIX_SIZE		512
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/* Channels on box are in reverse order */
3318c2ecf20Sopenharmony_cistatic unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int debug;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int s2255_start_readpipe(struct s2255_dev *dev);
3368c2ecf20Sopenharmony_cistatic void s2255_stop_readpipe(struct s2255_dev *dev);
3378c2ecf20Sopenharmony_cistatic int s2255_start_acquire(struct s2255_vc *vc);
3388c2ecf20Sopenharmony_cistatic int s2255_stop_acquire(struct s2255_vc *vc);
3398c2ecf20Sopenharmony_cistatic void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf,
3408c2ecf20Sopenharmony_ci			   int jpgsize);
3418c2ecf20Sopenharmony_cistatic int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode);
3428c2ecf20Sopenharmony_cistatic int s2255_board_shutdown(struct s2255_dev *dev);
3438c2ecf20Sopenharmony_cistatic void s2255_fwload_start(struct s2255_dev *dev);
3448c2ecf20Sopenharmony_cistatic void s2255_destroy(struct s2255_dev *dev);
3458c2ecf20Sopenharmony_cistatic long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
3468c2ecf20Sopenharmony_ci			     u16 index, u16 value, void *buf,
3478c2ecf20Sopenharmony_ci			     s32 buf_len, int bOut);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/* dev_err macro with driver name */
3508c2ecf20Sopenharmony_ci#define S2255_DRIVER_NAME "s2255"
3518c2ecf20Sopenharmony_ci#define s2255_dev_err(dev, fmt, arg...)					\
3528c2ecf20Sopenharmony_ci		dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci#define dprintk(dev, level, fmt, arg...) \
3558c2ecf20Sopenharmony_ci	v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic struct usb_driver s2255_driver;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/* start video number */
3608c2ecf20Sopenharmony_cistatic int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/* Enable jpeg capture. */
3638c2ecf20Sopenharmony_cistatic int jpeg_enable = 1;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
3668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
3678c2ecf20Sopenharmony_cimodule_param(video_nr, int, 0644);
3688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
3698c2ecf20Sopenharmony_cimodule_param(jpeg_enable, int, 0644);
3708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci/* USB device table */
3738c2ecf20Sopenharmony_ci#define USB_SENSORAY_VID	0x1943
3748c2ecf20Sopenharmony_cistatic const struct usb_device_id s2255_table[] = {
3758c2ecf20Sopenharmony_ci	{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
3768c2ecf20Sopenharmony_ci	{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
3778c2ecf20Sopenharmony_ci	{ }			/* Terminating entry */
3788c2ecf20Sopenharmony_ci};
3798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, s2255_table);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci#define BUFFER_TIMEOUT msecs_to_jiffies(400)
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/* image formats.  */
3848c2ecf20Sopenharmony_ci/* JPEG formats must be defined last to support jpeg_enable parameter */
3858c2ecf20Sopenharmony_cistatic const struct s2255_fmt formats[] = {
3868c2ecf20Sopenharmony_ci	{
3878c2ecf20Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_YUYV,
3888c2ecf20Sopenharmony_ci		.depth = 16
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	}, {
3918c2ecf20Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_UYVY,
3928c2ecf20Sopenharmony_ci		.depth = 16
3938c2ecf20Sopenharmony_ci	}, {
3948c2ecf20Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_YUV422P,
3958c2ecf20Sopenharmony_ci		.depth = 16
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	}, {
3988c2ecf20Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_GREY,
3998c2ecf20Sopenharmony_ci		.depth = 8
4008c2ecf20Sopenharmony_ci	}, {
4018c2ecf20Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_JPEG,
4028c2ecf20Sopenharmony_ci		.depth = 24
4038c2ecf20Sopenharmony_ci	}, {
4048c2ecf20Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_MJPEG,
4058c2ecf20Sopenharmony_ci		.depth = 24
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int norm_maxw(struct s2255_vc *vc)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
4128c2ecf20Sopenharmony_ci	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int norm_maxh(struct s2255_vc *vc)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
4188c2ecf20Sopenharmony_ci	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int norm_minw(struct s2255_vc *vc)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
4248c2ecf20Sopenharmony_ci	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int norm_minh(struct s2255_vc *vc)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
4308c2ecf20Sopenharmony_ci	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci/*
4358c2ecf20Sopenharmony_ci * TODO: fixme: move YUV reordering to hardware
4368c2ecf20Sopenharmony_ci * converts 2255 planar format to yuyv or uyvy
4378c2ecf20Sopenharmony_ci */
4388c2ecf20Sopenharmony_cistatic void planar422p_to_yuv_packed(const unsigned char *in,
4398c2ecf20Sopenharmony_ci				     unsigned char *out,
4408c2ecf20Sopenharmony_ci				     int width, int height,
4418c2ecf20Sopenharmony_ci				     int fmt)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	unsigned char *pY;
4448c2ecf20Sopenharmony_ci	unsigned char *pCb;
4458c2ecf20Sopenharmony_ci	unsigned char *pCr;
4468c2ecf20Sopenharmony_ci	unsigned long size = height * width;
4478c2ecf20Sopenharmony_ci	unsigned int i;
4488c2ecf20Sopenharmony_ci	pY = (unsigned char *)in;
4498c2ecf20Sopenharmony_ci	pCr = (unsigned char *)in + height * width;
4508c2ecf20Sopenharmony_ci	pCb = (unsigned char *)in + height * width + (height * width / 2);
4518c2ecf20Sopenharmony_ci	for (i = 0; i < size * 2; i += 4) {
4528c2ecf20Sopenharmony_ci		out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
4538c2ecf20Sopenharmony_ci		out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
4548c2ecf20Sopenharmony_ci		out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
4558c2ecf20Sopenharmony_ci		out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	return;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic void s2255_reset_dsppower(struct s2255_dev *dev)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
4638c2ecf20Sopenharmony_ci	msleep(50);
4648c2ecf20Sopenharmony_ci	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
4658c2ecf20Sopenharmony_ci	msleep(600);
4668c2ecf20Sopenharmony_ci	s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
4678c2ecf20Sopenharmony_ci	return;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/* kickstarts the firmware loading. from probe
4718c2ecf20Sopenharmony_ci */
4728c2ecf20Sopenharmony_cistatic void s2255_timer(struct timer_list *t)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct s2255_dev *dev = from_timer(dev, t, timer);
4758c2ecf20Sopenharmony_ci	struct s2255_fw *data = dev->fw_data;
4768c2ecf20Sopenharmony_ci	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
4778c2ecf20Sopenharmony_ci		pr_err("s2255: can't submit urb\n");
4788c2ecf20Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_FAILED);
4798c2ecf20Sopenharmony_ci		/* wake up anything waiting for the firmware */
4808c2ecf20Sopenharmony_ci		wake_up(&data->wait_fw);
4818c2ecf20Sopenharmony_ci		return;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/* this loads the firmware asynchronously.
4878c2ecf20Sopenharmony_ci   Originally this was done synchronously in probe.
4888c2ecf20Sopenharmony_ci   But it is better to load it asynchronously here than block
4898c2ecf20Sopenharmony_ci   inside the probe function. Blocking inside probe affects boot time.
4908c2ecf20Sopenharmony_ci   FW loading is triggered by the timer in the probe function
4918c2ecf20Sopenharmony_ci*/
4928c2ecf20Sopenharmony_cistatic void s2255_fwchunk_complete(struct urb *urb)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct s2255_fw *data = urb->context;
4958c2ecf20Sopenharmony_ci	struct usb_device *udev = urb->dev;
4968c2ecf20Sopenharmony_ci	int len;
4978c2ecf20Sopenharmony_ci	if (urb->status) {
4988c2ecf20Sopenharmony_ci		dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
4998c2ecf20Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_FAILED);
5008c2ecf20Sopenharmony_ci		/* wake up anything waiting for the firmware */
5018c2ecf20Sopenharmony_ci		wake_up(&data->wait_fw);
5028c2ecf20Sopenharmony_ci		return;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	if (data->fw_urb == NULL) {
5058c2ecf20Sopenharmony_ci		s2255_dev_err(&udev->dev, "disconnected\n");
5068c2ecf20Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_FAILED);
5078c2ecf20Sopenharmony_ci		/* wake up anything waiting for the firmware */
5088c2ecf20Sopenharmony_ci		wake_up(&data->wait_fw);
5098c2ecf20Sopenharmony_ci		return;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci#define CHUNK_SIZE 512
5128c2ecf20Sopenharmony_ci	/* all USB transfers must be done with continuous kernel memory.
5138c2ecf20Sopenharmony_ci	   can't allocate more than 128k in current linux kernel, so
5148c2ecf20Sopenharmony_ci	   upload the firmware in chunks
5158c2ecf20Sopenharmony_ci	 */
5168c2ecf20Sopenharmony_ci	if (data->fw_loaded < data->fw_size) {
5178c2ecf20Sopenharmony_ci		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
5188c2ecf20Sopenharmony_ci		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		if (len < CHUNK_SIZE)
5218c2ecf20Sopenharmony_ci			memset(data->pfw_data, 0, CHUNK_SIZE);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		memcpy(data->pfw_data,
5248c2ecf20Sopenharmony_ci		       (char *) data->fw->data + data->fw_loaded, len);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
5278c2ecf20Sopenharmony_ci				  data->pfw_data, CHUNK_SIZE,
5288c2ecf20Sopenharmony_ci				  s2255_fwchunk_complete, data);
5298c2ecf20Sopenharmony_ci		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
5308c2ecf20Sopenharmony_ci			dev_err(&udev->dev, "failed submit URB\n");
5318c2ecf20Sopenharmony_ci			atomic_set(&data->fw_state, S2255_FW_FAILED);
5328c2ecf20Sopenharmony_ci			/* wake up anything waiting for the firmware */
5338c2ecf20Sopenharmony_ci			wake_up(&data->wait_fw);
5348c2ecf20Sopenharmony_ci			return;
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci		data->fw_loaded += len;
5378c2ecf20Sopenharmony_ci	} else
5388c2ecf20Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
5398c2ecf20Sopenharmony_ci	return;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic void s2255_got_frame(struct s2255_vc *vc, int jpgsize)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct s2255_buffer *buf;
5468c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
5478c2ecf20Sopenharmony_ci	unsigned long flags = 0;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
5508c2ecf20Sopenharmony_ci	if (list_empty(&vc->buf_list)) {
5518c2ecf20Sopenharmony_ci		dprintk(dev, 1, "No active queue to serve\n");
5528c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vc->qlock, flags);
5538c2ecf20Sopenharmony_ci		return;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci	buf = list_entry(vc->buf_list.next,
5568c2ecf20Sopenharmony_ci			 struct s2255_buffer, list);
5578c2ecf20Sopenharmony_ci	list_del(&buf->list);
5588c2ecf20Sopenharmony_ci	buf->vb.vb2_buf.timestamp = ktime_get_ns();
5598c2ecf20Sopenharmony_ci	buf->vb.field = vc->field;
5608c2ecf20Sopenharmony_ci	buf->vb.sequence = vc->frame_count;
5618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	s2255_fillbuff(vc, buf, jpgsize);
5648c2ecf20Sopenharmony_ci	/* tell v4l buffer was filled */
5658c2ecf20Sopenharmony_ci	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
5668c2ecf20Sopenharmony_ci	dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic const struct s2255_fmt *format_by_fourcc(int fourcc)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	unsigned int i;
5728c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++) {
5738c2ecf20Sopenharmony_ci		if (-1 == formats[i].fourcc)
5748c2ecf20Sopenharmony_ci			continue;
5758c2ecf20Sopenharmony_ci		if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
5768c2ecf20Sopenharmony_ci				     (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
5778c2ecf20Sopenharmony_ci			continue;
5788c2ecf20Sopenharmony_ci		if (formats[i].fourcc == fourcc)
5798c2ecf20Sopenharmony_ci			return formats + i;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci	return NULL;
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci/* video buffer vmalloc implementation based partly on VIVI driver which is
5858c2ecf20Sopenharmony_ci *          Copyright (c) 2006 by
5868c2ecf20Sopenharmony_ci *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
5878c2ecf20Sopenharmony_ci *                  Ted Walther <ted--a.t--enumera.com>
5888c2ecf20Sopenharmony_ci *                  John Sokol <sokol--a.t--videotechnology.com>
5898c2ecf20Sopenharmony_ci *                  http://v4l.videotechnology.com/
5908c2ecf20Sopenharmony_ci *
5918c2ecf20Sopenharmony_ci */
5928c2ecf20Sopenharmony_cistatic void s2255_fillbuff(struct s2255_vc *vc,
5938c2ecf20Sopenharmony_ci			   struct s2255_buffer *buf, int jpgsize)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	int pos = 0;
5968c2ecf20Sopenharmony_ci	const char *tmpbuf;
5978c2ecf20Sopenharmony_ci	char *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
5988c2ecf20Sopenharmony_ci	unsigned long last_frame;
5998c2ecf20Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (!vbuf)
6028c2ecf20Sopenharmony_ci		return;
6038c2ecf20Sopenharmony_ci	last_frame = vc->last_frame;
6048c2ecf20Sopenharmony_ci	if (last_frame != -1) {
6058c2ecf20Sopenharmony_ci		tmpbuf =
6068c2ecf20Sopenharmony_ci		    (const char *)vc->buffer.frame[last_frame].lpvbits;
6078c2ecf20Sopenharmony_ci		switch (vc->fmt->fourcc) {
6088c2ecf20Sopenharmony_ci		case V4L2_PIX_FMT_YUYV:
6098c2ecf20Sopenharmony_ci		case V4L2_PIX_FMT_UYVY:
6108c2ecf20Sopenharmony_ci			planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
6118c2ecf20Sopenharmony_ci						 vbuf, vc->width,
6128c2ecf20Sopenharmony_ci						 vc->height,
6138c2ecf20Sopenharmony_ci						 vc->fmt->fourcc);
6148c2ecf20Sopenharmony_ci			break;
6158c2ecf20Sopenharmony_ci		case V4L2_PIX_FMT_GREY:
6168c2ecf20Sopenharmony_ci			memcpy(vbuf, tmpbuf, vc->width * vc->height);
6178c2ecf20Sopenharmony_ci			break;
6188c2ecf20Sopenharmony_ci		case V4L2_PIX_FMT_JPEG:
6198c2ecf20Sopenharmony_ci		case V4L2_PIX_FMT_MJPEG:
6208c2ecf20Sopenharmony_ci			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
6218c2ecf20Sopenharmony_ci			memcpy(vbuf, tmpbuf, jpgsize);
6228c2ecf20Sopenharmony_ci			break;
6238c2ecf20Sopenharmony_ci		case V4L2_PIX_FMT_YUV422P:
6248c2ecf20Sopenharmony_ci			memcpy(vbuf, tmpbuf,
6258c2ecf20Sopenharmony_ci			       vc->width * vc->height * 2);
6268c2ecf20Sopenharmony_ci			break;
6278c2ecf20Sopenharmony_ci		default:
6288c2ecf20Sopenharmony_ci			pr_info("s2255: unknown format?\n");
6298c2ecf20Sopenharmony_ci		}
6308c2ecf20Sopenharmony_ci		vc->last_frame = -1;
6318c2ecf20Sopenharmony_ci	} else {
6328c2ecf20Sopenharmony_ci		pr_err("s2255: =======no frame\n");
6338c2ecf20Sopenharmony_ci		return;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci	dprintk(dev, 2, "s2255fill at : Buffer %p size= %d\n",
6368c2ecf20Sopenharmony_ci		vbuf, pos);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
6418c2ecf20Sopenharmony_ci   Videobuf operations
6428c2ecf20Sopenharmony_ci   ------------------------------------------------------------------*/
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
6458c2ecf20Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
6468c2ecf20Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vq);
6498c2ecf20Sopenharmony_ci	if (*nbuffers < S2255_MIN_BUFS)
6508c2ecf20Sopenharmony_ci		*nbuffers = S2255_MIN_BUFS;
6518c2ecf20Sopenharmony_ci	*nplanes = 1;
6528c2ecf20Sopenharmony_ci	sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3);
6538c2ecf20Sopenharmony_ci	return 0;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
6598c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6608c2ecf20Sopenharmony_ci	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
6618c2ecf20Sopenharmony_ci	int w = vc->width;
6628c2ecf20Sopenharmony_ci	int h = vc->height;
6638c2ecf20Sopenharmony_ci	unsigned long size;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	dprintk(vc->dev, 4, "%s\n", __func__);
6668c2ecf20Sopenharmony_ci	if (vc->fmt == NULL)
6678c2ecf20Sopenharmony_ci		return -EINVAL;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if ((w < norm_minw(vc)) ||
6708c2ecf20Sopenharmony_ci	    (w > norm_maxw(vc)) ||
6718c2ecf20Sopenharmony_ci	    (h < norm_minh(vc)) ||
6728c2ecf20Sopenharmony_ci	    (h > norm_maxh(vc))) {
6738c2ecf20Sopenharmony_ci		dprintk(vc->dev, 4, "invalid buffer prepare\n");
6748c2ecf20Sopenharmony_ci		return -EINVAL;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci	size = w * h * (vc->fmt->depth >> 3);
6778c2ecf20Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
6788c2ecf20Sopenharmony_ci		dprintk(vc->dev, 4, "invalid buffer prepare\n");
6798c2ecf20Sopenharmony_ci		return -EINVAL;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
6838c2ecf20Sopenharmony_ci	return 0;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
6898c2ecf20Sopenharmony_ci	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
6908c2ecf20Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
6918c2ecf20Sopenharmony_ci	unsigned long flags = 0;
6928c2ecf20Sopenharmony_ci	dprintk(vc->dev, 1, "%s\n", __func__);
6938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
6948c2ecf20Sopenharmony_ci	list_add_tail(&buf->list, &vc->buf_list);
6958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count);
6998c2ecf20Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic const struct vb2_ops s2255_video_qops = {
7028c2ecf20Sopenharmony_ci	.queue_setup = queue_setup,
7038c2ecf20Sopenharmony_ci	.buf_prepare = buffer_prepare,
7048c2ecf20Sopenharmony_ci	.buf_queue = buffer_queue,
7058c2ecf20Sopenharmony_ci	.start_streaming = start_streaming,
7068c2ecf20Sopenharmony_ci	.stop_streaming = stop_streaming,
7078c2ecf20Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
7088c2ecf20Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
7098c2ecf20Sopenharmony_ci};
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv,
7128c2ecf20Sopenharmony_ci			   struct v4l2_capability *cap)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
7158c2ecf20Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	strscpy(cap->driver, "s2255", sizeof(cap->driver));
7188c2ecf20Sopenharmony_ci	strscpy(cap->card, "s2255", sizeof(cap->card));
7198c2ecf20Sopenharmony_ci	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
7208c2ecf20Sopenharmony_ci	return 0;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
7248c2ecf20Sopenharmony_ci			       struct v4l2_fmtdesc *f)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	int index = f->index;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (index >= ARRAY_SIZE(formats))
7298c2ecf20Sopenharmony_ci		return -EINVAL;
7308c2ecf20Sopenharmony_ci	if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
7318c2ecf20Sopenharmony_ci			(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
7328c2ecf20Sopenharmony_ci		return -EINVAL;
7338c2ecf20Sopenharmony_ci	f->pixelformat = formats[index].fourcc;
7348c2ecf20Sopenharmony_ci	return 0;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
7388c2ecf20Sopenharmony_ci			    struct v4l2_format *f)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
7418c2ecf20Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	f->fmt.pix.width = vc->width;
7448c2ecf20Sopenharmony_ci	f->fmt.pix.height = vc->height;
7458c2ecf20Sopenharmony_ci	if (f->fmt.pix.height >=
7468c2ecf20Sopenharmony_ci	    (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2)
7478c2ecf20Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
7488c2ecf20Sopenharmony_ci	else
7498c2ecf20Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_TOP;
7508c2ecf20Sopenharmony_ci	f->fmt.pix.pixelformat = vc->fmt->fourcc;
7518c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3);
7528c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
7538c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
7548c2ecf20Sopenharmony_ci	return 0;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
7588c2ecf20Sopenharmony_ci			      struct v4l2_format *f)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	const struct s2255_fmt *fmt;
7618c2ecf20Sopenharmony_ci	enum v4l2_field field;
7628c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
7638c2ecf20Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (fmt == NULL)
7688c2ecf20Sopenharmony_ci		return -EINVAL;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	field = f->fmt.pix.field;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n",
7738c2ecf20Sopenharmony_ci		__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
7748c2ecf20Sopenharmony_ci	if (is_ntsc) {
7758c2ecf20Sopenharmony_ci		/* NTSC */
7768c2ecf20Sopenharmony_ci		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
7778c2ecf20Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
7788c2ecf20Sopenharmony_ci			field = V4L2_FIELD_INTERLACED;
7798c2ecf20Sopenharmony_ci		} else {
7808c2ecf20Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
7818c2ecf20Sopenharmony_ci			field = V4L2_FIELD_TOP;
7828c2ecf20Sopenharmony_ci		}
7838c2ecf20Sopenharmony_ci		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
7848c2ecf20Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
7858c2ecf20Sopenharmony_ci		else
7868c2ecf20Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
7878c2ecf20Sopenharmony_ci	} else {
7888c2ecf20Sopenharmony_ci		/* PAL */
7898c2ecf20Sopenharmony_ci		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
7908c2ecf20Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
7918c2ecf20Sopenharmony_ci			field = V4L2_FIELD_INTERLACED;
7928c2ecf20Sopenharmony_ci		} else {
7938c2ecf20Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
7948c2ecf20Sopenharmony_ci			field = V4L2_FIELD_TOP;
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL)
7978c2ecf20Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
7988c2ecf20Sopenharmony_ci		else
7998c2ecf20Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci	f->fmt.pix.field = field;
8028c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
8038c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
8048c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
8058c2ecf20Sopenharmony_ci	dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__,
8068c2ecf20Sopenharmony_ci		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
8078c2ecf20Sopenharmony_ci	return 0;
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
8118c2ecf20Sopenharmony_ci			    struct v4l2_format *f)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
8148c2ecf20Sopenharmony_ci	const struct s2255_fmt *fmt;
8158c2ecf20Sopenharmony_ci	struct vb2_queue *q = &vc->vb_vidq;
8168c2ecf20Sopenharmony_ci	struct s2255_mode mode;
8178c2ecf20Sopenharmony_ci	int ret;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	ret = vidioc_try_fmt_vid_cap(file, vc, f);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (ret < 0)
8228c2ecf20Sopenharmony_ci		return ret;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (fmt == NULL)
8278c2ecf20Sopenharmony_ci		return -EINVAL;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (vb2_is_busy(q)) {
8308c2ecf20Sopenharmony_ci		dprintk(vc->dev, 1, "queue busy\n");
8318c2ecf20Sopenharmony_ci		return -EBUSY;
8328c2ecf20Sopenharmony_ci	}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	mode = vc->mode;
8358c2ecf20Sopenharmony_ci	vc->fmt = fmt;
8368c2ecf20Sopenharmony_ci	vc->width = f->fmt.pix.width;
8378c2ecf20Sopenharmony_ci	vc->height = f->fmt.pix.height;
8388c2ecf20Sopenharmony_ci	vc->field = f->fmt.pix.field;
8398c2ecf20Sopenharmony_ci	if (vc->width > norm_minw(vc)) {
8408c2ecf20Sopenharmony_ci		if (vc->height > norm_minh(vc)) {
8418c2ecf20Sopenharmony_ci			if (vc->cap_parm.capturemode &
8428c2ecf20Sopenharmony_ci			    V4L2_MODE_HIGHQUALITY)
8438c2ecf20Sopenharmony_ci				mode.scale = SCALE_4CIFSI;
8448c2ecf20Sopenharmony_ci			else
8458c2ecf20Sopenharmony_ci				mode.scale = SCALE_4CIFS;
8468c2ecf20Sopenharmony_ci		} else
8478c2ecf20Sopenharmony_ci			mode.scale = SCALE_2CIFS;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	} else {
8508c2ecf20Sopenharmony_ci		mode.scale = SCALE_1CIFS;
8518c2ecf20Sopenharmony_ci	}
8528c2ecf20Sopenharmony_ci	/* color mode */
8538c2ecf20Sopenharmony_ci	switch (vc->fmt->fourcc) {
8548c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
8558c2ecf20Sopenharmony_ci		mode.color &= ~MASK_COLOR;
8568c2ecf20Sopenharmony_ci		mode.color |= COLOR_Y8;
8578c2ecf20Sopenharmony_ci		break;
8588c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_JPEG:
8598c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_MJPEG:
8608c2ecf20Sopenharmony_ci		mode.color &= ~MASK_COLOR;
8618c2ecf20Sopenharmony_ci		mode.color |= COLOR_JPG;
8628c2ecf20Sopenharmony_ci		mode.color |= (vc->jpegqual << 8);
8638c2ecf20Sopenharmony_ci		break;
8648c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
8658c2ecf20Sopenharmony_ci		mode.color &= ~MASK_COLOR;
8668c2ecf20Sopenharmony_ci		mode.color |= COLOR_YUVPL;
8678c2ecf20Sopenharmony_ci		break;
8688c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
8698c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
8708c2ecf20Sopenharmony_ci	default:
8718c2ecf20Sopenharmony_ci		mode.color &= ~MASK_COLOR;
8728c2ecf20Sopenharmony_ci		mode.color |= COLOR_YUVPK;
8738c2ecf20Sopenharmony_ci		break;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci	if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR))
8768c2ecf20Sopenharmony_ci		mode.restart = 1;
8778c2ecf20Sopenharmony_ci	else if (mode.scale != vc->mode.scale)
8788c2ecf20Sopenharmony_ci		mode.restart = 1;
8798c2ecf20Sopenharmony_ci	else if (mode.format != vc->mode.format)
8808c2ecf20Sopenharmony_ci		mode.restart = 1;
8818c2ecf20Sopenharmony_ci	vc->mode = mode;
8828c2ecf20Sopenharmony_ci	(void) s2255_set_mode(vc, &mode);
8838c2ecf20Sopenharmony_ci	return 0;
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci/* write to the configuration pipe, synchronously */
8888c2ecf20Sopenharmony_cistatic int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
8898c2ecf20Sopenharmony_ci			      int size)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	int pipe;
8928c2ecf20Sopenharmony_ci	int done;
8938c2ecf20Sopenharmony_ci	long retval = -1;
8948c2ecf20Sopenharmony_ci	if (udev) {
8958c2ecf20Sopenharmony_ci		pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
8968c2ecf20Sopenharmony_ci		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
8978c2ecf20Sopenharmony_ci	}
8988c2ecf20Sopenharmony_ci	return retval;
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic u32 get_transfer_size(struct s2255_mode *mode)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	int linesPerFrame = LINE_SZ_DEF;
9048c2ecf20Sopenharmony_ci	int pixelsPerLine = NUM_LINES_DEF;
9058c2ecf20Sopenharmony_ci	u32 outImageSize;
9068c2ecf20Sopenharmony_ci	u32 usbInSize;
9078c2ecf20Sopenharmony_ci	unsigned int mask_mult;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (mode == NULL)
9108c2ecf20Sopenharmony_ci		return 0;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (mode->format == FORMAT_NTSC) {
9138c2ecf20Sopenharmony_ci		switch (mode->scale) {
9148c2ecf20Sopenharmony_ci		case SCALE_4CIFS:
9158c2ecf20Sopenharmony_ci		case SCALE_4CIFSI:
9168c2ecf20Sopenharmony_ci			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
9178c2ecf20Sopenharmony_ci			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
9188c2ecf20Sopenharmony_ci			break;
9198c2ecf20Sopenharmony_ci		case SCALE_2CIFS:
9208c2ecf20Sopenharmony_ci			linesPerFrame = NUM_LINES_2CIFS_NTSC;
9218c2ecf20Sopenharmony_ci			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
9228c2ecf20Sopenharmony_ci			break;
9238c2ecf20Sopenharmony_ci		case SCALE_1CIFS:
9248c2ecf20Sopenharmony_ci			linesPerFrame = NUM_LINES_1CIFS_NTSC;
9258c2ecf20Sopenharmony_ci			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
9268c2ecf20Sopenharmony_ci			break;
9278c2ecf20Sopenharmony_ci		default:
9288c2ecf20Sopenharmony_ci			break;
9298c2ecf20Sopenharmony_ci		}
9308c2ecf20Sopenharmony_ci	} else if (mode->format == FORMAT_PAL) {
9318c2ecf20Sopenharmony_ci		switch (mode->scale) {
9328c2ecf20Sopenharmony_ci		case SCALE_4CIFS:
9338c2ecf20Sopenharmony_ci		case SCALE_4CIFSI:
9348c2ecf20Sopenharmony_ci			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
9358c2ecf20Sopenharmony_ci			pixelsPerLine = LINE_SZ_4CIFS_PAL;
9368c2ecf20Sopenharmony_ci			break;
9378c2ecf20Sopenharmony_ci		case SCALE_2CIFS:
9388c2ecf20Sopenharmony_ci			linesPerFrame = NUM_LINES_2CIFS_PAL;
9398c2ecf20Sopenharmony_ci			pixelsPerLine = LINE_SZ_2CIFS_PAL;
9408c2ecf20Sopenharmony_ci			break;
9418c2ecf20Sopenharmony_ci		case SCALE_1CIFS:
9428c2ecf20Sopenharmony_ci			linesPerFrame = NUM_LINES_1CIFS_PAL;
9438c2ecf20Sopenharmony_ci			pixelsPerLine = LINE_SZ_1CIFS_PAL;
9448c2ecf20Sopenharmony_ci			break;
9458c2ecf20Sopenharmony_ci		default:
9468c2ecf20Sopenharmony_ci			break;
9478c2ecf20Sopenharmony_ci		}
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci	outImageSize = linesPerFrame * pixelsPerLine;
9508c2ecf20Sopenharmony_ci	if ((mode->color & MASK_COLOR) != COLOR_Y8) {
9518c2ecf20Sopenharmony_ci		/* 2 bytes/pixel if not monochrome */
9528c2ecf20Sopenharmony_ci		outImageSize *= 2;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	/* total bytes to send including prefix and 4K padding;
9568c2ecf20Sopenharmony_ci	   must be a multiple of USB_READ_SIZE */
9578c2ecf20Sopenharmony_ci	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
9588c2ecf20Sopenharmony_ci	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
9598c2ecf20Sopenharmony_ci	/* if size not a multiple of USB_READ_SIZE */
9608c2ecf20Sopenharmony_ci	if (usbInSize & ~mask_mult)
9618c2ecf20Sopenharmony_ci		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
9628c2ecf20Sopenharmony_ci	return usbInSize;
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cistatic void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	struct device *dev = &sdev->udev->dev;
9688c2ecf20Sopenharmony_ci	dev_info(dev, "------------------------------------------------\n");
9698c2ecf20Sopenharmony_ci	dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
9708c2ecf20Sopenharmony_ci	dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
9718c2ecf20Sopenharmony_ci	dev_info(dev, "bright: 0x%x\n", mode->bright);
9728c2ecf20Sopenharmony_ci	dev_info(dev, "------------------------------------------------\n");
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci/*
9768c2ecf20Sopenharmony_ci * set mode is the function which controls the DSP.
9778c2ecf20Sopenharmony_ci * the restart parameter in struct s2255_mode should be set whenever
9788c2ecf20Sopenharmony_ci * the image size could change via color format, video system or image
9798c2ecf20Sopenharmony_ci * size.
9808c2ecf20Sopenharmony_ci * When the restart parameter is set, we sleep for ONE frame to allow the
9818c2ecf20Sopenharmony_ci * DSP time to get the new frame
9828c2ecf20Sopenharmony_ci */
9838c2ecf20Sopenharmony_cistatic int s2255_set_mode(struct s2255_vc *vc,
9848c2ecf20Sopenharmony_ci			  struct s2255_mode *mode)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	int res;
9878c2ecf20Sopenharmony_ci	unsigned long chn_rev;
9888c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
9898c2ecf20Sopenharmony_ci	int i;
9908c2ecf20Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	mutex_lock(&dev->cmdlock);
9938c2ecf20Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
9948c2ecf20Sopenharmony_ci	dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx);
9958c2ecf20Sopenharmony_ci	/* if JPEG, set the quality */
9968c2ecf20Sopenharmony_ci	if ((mode->color & MASK_COLOR) == COLOR_JPG) {
9978c2ecf20Sopenharmony_ci		mode->color &= ~MASK_COLOR;
9988c2ecf20Sopenharmony_ci		mode->color |= COLOR_JPG;
9998c2ecf20Sopenharmony_ci		mode->color &= ~MASK_JPG_QUALITY;
10008c2ecf20Sopenharmony_ci		mode->color |= (vc->jpegqual << 8);
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci	/* save the mode */
10038c2ecf20Sopenharmony_ci	vc->mode = *mode;
10048c2ecf20Sopenharmony_ci	vc->req_image_size = get_transfer_size(mode);
10058c2ecf20Sopenharmony_ci	dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size);
10068c2ecf20Sopenharmony_ci	/* set the mode */
10078c2ecf20Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
10088c2ecf20Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
10098c2ecf20Sopenharmony_ci	buffer[2] = CMD_SET_MODE;
10108c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++)
10118c2ecf20Sopenharmony_ci		buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]);
10128c2ecf20Sopenharmony_ci	vc->setmode_ready = 0;
10138c2ecf20Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
10148c2ecf20Sopenharmony_ci	if (debug)
10158c2ecf20Sopenharmony_ci		s2255_print_cfg(dev, mode);
10168c2ecf20Sopenharmony_ci	/* wait at least 3 frames before continuing */
10178c2ecf20Sopenharmony_ci	if (mode->restart) {
10188c2ecf20Sopenharmony_ci		wait_event_timeout(vc->wait_setmode,
10198c2ecf20Sopenharmony_ci				   (vc->setmode_ready != 0),
10208c2ecf20Sopenharmony_ci				   msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
10218c2ecf20Sopenharmony_ci		if (vc->setmode_ready != 1) {
10228c2ecf20Sopenharmony_ci			dprintk(dev, 0, "s2255: no set mode response\n");
10238c2ecf20Sopenharmony_ci			res = -EFAULT;
10248c2ecf20Sopenharmony_ci		}
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci	/* clear the restart flag */
10278c2ecf20Sopenharmony_ci	vc->mode.restart = 0;
10288c2ecf20Sopenharmony_ci	dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res);
10298c2ecf20Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
10308c2ecf20Sopenharmony_ci	return res;
10318c2ecf20Sopenharmony_ci}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_cistatic int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus)
10348c2ecf20Sopenharmony_ci{
10358c2ecf20Sopenharmony_ci	int res;
10368c2ecf20Sopenharmony_ci	u32 chn_rev;
10378c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
10388c2ecf20Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	mutex_lock(&dev->cmdlock);
10418c2ecf20Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
10428c2ecf20Sopenharmony_ci	dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx);
10438c2ecf20Sopenharmony_ci	/* form the get vid status command */
10448c2ecf20Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
10458c2ecf20Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
10468c2ecf20Sopenharmony_ci	buffer[2] = CMD_STATUS;
10478c2ecf20Sopenharmony_ci	*pstatus = 0;
10488c2ecf20Sopenharmony_ci	vc->vidstatus_ready = 0;
10498c2ecf20Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
10508c2ecf20Sopenharmony_ci	wait_event_timeout(vc->wait_vidstatus,
10518c2ecf20Sopenharmony_ci			   (vc->vidstatus_ready != 0),
10528c2ecf20Sopenharmony_ci			   msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
10538c2ecf20Sopenharmony_ci	if (vc->vidstatus_ready != 1) {
10548c2ecf20Sopenharmony_ci		dprintk(dev, 0, "s2255: no vidstatus response\n");
10558c2ecf20Sopenharmony_ci		res = -EFAULT;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci	*pstatus = vc->vidstatus;
10588c2ecf20Sopenharmony_ci	dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus);
10598c2ecf20Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
10608c2ecf20Sopenharmony_ci	return res;
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vq);
10668c2ecf20Sopenharmony_ci	int j;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	vc->last_frame = -1;
10698c2ecf20Sopenharmony_ci	vc->bad_payload = 0;
10708c2ecf20Sopenharmony_ci	vc->cur_frame = 0;
10718c2ecf20Sopenharmony_ci	vc->frame_count = 0;
10728c2ecf20Sopenharmony_ci	for (j = 0; j < SYS_FRAMES; j++) {
10738c2ecf20Sopenharmony_ci		vc->buffer.frame[j].ulState = S2255_READ_IDLE;
10748c2ecf20Sopenharmony_ci		vc->buffer.frame[j].cur_size = 0;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci	return s2255_start_acquire(vc);
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */
10808c2ecf20Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vq);
10838c2ecf20Sopenharmony_ci	struct s2255_buffer *buf, *node;
10848c2ecf20Sopenharmony_ci	unsigned long flags;
10858c2ecf20Sopenharmony_ci	(void) s2255_stop_acquire(vc);
10868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
10878c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buf, node, &vc->buf_list, list) {
10888c2ecf20Sopenharmony_ci		list_del(&buf->list);
10898c2ecf20Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
10908c2ecf20Sopenharmony_ci		dprintk(vc->dev, 2, "[%p/%d] done\n",
10918c2ecf20Sopenharmony_ci			buf, buf->vb.vb2_buf.index);
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
10998c2ecf20Sopenharmony_ci	struct s2255_mode mode;
11008c2ecf20Sopenharmony_ci	struct vb2_queue *q = &vc->vb_vidq;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	/*
11038c2ecf20Sopenharmony_ci	 * Changing the standard implies a format change, which is not allowed
11048c2ecf20Sopenharmony_ci	 * while buffers for use with streaming have already been allocated.
11058c2ecf20Sopenharmony_ci	 */
11068c2ecf20Sopenharmony_ci	if (vb2_is_busy(q))
11078c2ecf20Sopenharmony_ci		return -EBUSY;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	mode = vc->mode;
11108c2ecf20Sopenharmony_ci	if (i & V4L2_STD_525_60) {
11118c2ecf20Sopenharmony_ci		dprintk(vc->dev, 4, "%s 60 Hz\n", __func__);
11128c2ecf20Sopenharmony_ci		/* if changing format, reset frame decimation/intervals */
11138c2ecf20Sopenharmony_ci		if (mode.format != FORMAT_NTSC) {
11148c2ecf20Sopenharmony_ci			mode.restart = 1;
11158c2ecf20Sopenharmony_ci			mode.format = FORMAT_NTSC;
11168c2ecf20Sopenharmony_ci			mode.fdec = FDEC_1;
11178c2ecf20Sopenharmony_ci			vc->width = LINE_SZ_4CIFS_NTSC;
11188c2ecf20Sopenharmony_ci			vc->height = NUM_LINES_4CIFS_NTSC * 2;
11198c2ecf20Sopenharmony_ci		}
11208c2ecf20Sopenharmony_ci	} else if (i & V4L2_STD_625_50) {
11218c2ecf20Sopenharmony_ci		dprintk(vc->dev, 4, "%s 50 Hz\n", __func__);
11228c2ecf20Sopenharmony_ci		if (mode.format != FORMAT_PAL) {
11238c2ecf20Sopenharmony_ci			mode.restart = 1;
11248c2ecf20Sopenharmony_ci			mode.format = FORMAT_PAL;
11258c2ecf20Sopenharmony_ci			mode.fdec = FDEC_1;
11268c2ecf20Sopenharmony_ci			vc->width = LINE_SZ_4CIFS_PAL;
11278c2ecf20Sopenharmony_ci			vc->height = NUM_LINES_4CIFS_PAL * 2;
11288c2ecf20Sopenharmony_ci		}
11298c2ecf20Sopenharmony_ci	} else
11308c2ecf20Sopenharmony_ci		return -EINVAL;
11318c2ecf20Sopenharmony_ci	vc->std = i;
11328c2ecf20Sopenharmony_ci	if (mode.restart)
11338c2ecf20Sopenharmony_ci		s2255_set_mode(vc, &mode);
11348c2ecf20Sopenharmony_ci	return 0;
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	*i = vc->std;
11428c2ecf20Sopenharmony_ci	return 0;
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci/* Sensoray 2255 is a multiple channel capture device.
11468c2ecf20Sopenharmony_ci   It does not have a "crossbar" of inputs.
11478c2ecf20Sopenharmony_ci   We use one V4L device per channel. The user must
11488c2ecf20Sopenharmony_ci   be aware that certain combinations are not allowed.
11498c2ecf20Sopenharmony_ci   For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
11508c2ecf20Sopenharmony_ci   at once in color(you can do full fps on 4 channels with greyscale.
11518c2ecf20Sopenharmony_ci*/
11528c2ecf20Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
11538c2ecf20Sopenharmony_ci			     struct v4l2_input *inp)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
11568c2ecf20Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
11578c2ecf20Sopenharmony_ci	u32 status = 0;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	if (inp->index != 0)
11608c2ecf20Sopenharmony_ci		return -EINVAL;
11618c2ecf20Sopenharmony_ci	inp->type = V4L2_INPUT_TYPE_CAMERA;
11628c2ecf20Sopenharmony_ci	inp->std = S2255_NORMS;
11638c2ecf20Sopenharmony_ci	inp->status = 0;
11648c2ecf20Sopenharmony_ci	if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
11658c2ecf20Sopenharmony_ci		int rc;
11668c2ecf20Sopenharmony_ci		rc = s2255_cmd_status(vc, &status);
11678c2ecf20Sopenharmony_ci		dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n",
11688c2ecf20Sopenharmony_ci			rc, status);
11698c2ecf20Sopenharmony_ci		if (rc == 0)
11708c2ecf20Sopenharmony_ci			inp->status =  (status & 0x01) ? 0
11718c2ecf20Sopenharmony_ci				: V4L2_IN_ST_NO_SIGNAL;
11728c2ecf20Sopenharmony_ci	}
11738c2ecf20Sopenharmony_ci	switch (dev->pid) {
11748c2ecf20Sopenharmony_ci	case 0x2255:
11758c2ecf20Sopenharmony_ci	default:
11768c2ecf20Sopenharmony_ci		strscpy(inp->name, "Composite", sizeof(inp->name));
11778c2ecf20Sopenharmony_ci		break;
11788c2ecf20Sopenharmony_ci	case 0x2257:
11798c2ecf20Sopenharmony_ci		strscpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video",
11808c2ecf20Sopenharmony_ci			sizeof(inp->name));
11818c2ecf20Sopenharmony_ci		break;
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci	return 0;
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	*i = 0;
11898c2ecf20Sopenharmony_ci	return 0;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
11928c2ecf20Sopenharmony_ci{
11938c2ecf20Sopenharmony_ci	if (i > 0)
11948c2ecf20Sopenharmony_ci		return -EINVAL;
11958c2ecf20Sopenharmony_ci	return 0;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct s2255_vc *vc =
12018c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct s2255_vc, hdl);
12028c2ecf20Sopenharmony_ci	struct s2255_mode mode;
12038c2ecf20Sopenharmony_ci	mode = vc->mode;
12048c2ecf20Sopenharmony_ci	/* update the mode to the corresponding value */
12058c2ecf20Sopenharmony_ci	switch (ctrl->id) {
12068c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
12078c2ecf20Sopenharmony_ci		mode.bright = ctrl->val;
12088c2ecf20Sopenharmony_ci		break;
12098c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
12108c2ecf20Sopenharmony_ci		mode.contrast = ctrl->val;
12118c2ecf20Sopenharmony_ci		break;
12128c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
12138c2ecf20Sopenharmony_ci		mode.hue = ctrl->val;
12148c2ecf20Sopenharmony_ci		break;
12158c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
12168c2ecf20Sopenharmony_ci		mode.saturation = ctrl->val;
12178c2ecf20Sopenharmony_ci		break;
12188c2ecf20Sopenharmony_ci	case V4L2_CID_S2255_COLORFILTER:
12198c2ecf20Sopenharmony_ci		mode.color &= ~MASK_INPUT_TYPE;
12208c2ecf20Sopenharmony_ci		mode.color |= !ctrl->val << 16;
12218c2ecf20Sopenharmony_ci		break;
12228c2ecf20Sopenharmony_ci	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
12238c2ecf20Sopenharmony_ci		vc->jpegqual = ctrl->val;
12248c2ecf20Sopenharmony_ci		return 0;
12258c2ecf20Sopenharmony_ci	default:
12268c2ecf20Sopenharmony_ci		return -EINVAL;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci	mode.restart = 0;
12298c2ecf20Sopenharmony_ci	/* set mode here.  Note: stream does not need restarted.
12308c2ecf20Sopenharmony_ci	   some V4L programs restart stream unnecessarily
12318c2ecf20Sopenharmony_ci	   after a s_crtl.
12328c2ecf20Sopenharmony_ci	*/
12338c2ecf20Sopenharmony_ci	s2255_set_mode(vc, &mode);
12348c2ecf20Sopenharmony_ci	return 0;
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic int vidioc_g_jpegcomp(struct file *file, void *priv,
12388c2ecf20Sopenharmony_ci			 struct v4l2_jpegcompression *jc)
12398c2ecf20Sopenharmony_ci{
12408c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	memset(jc, 0, sizeof(*jc));
12438c2ecf20Sopenharmony_ci	jc->quality = vc->jpegqual;
12448c2ecf20Sopenharmony_ci	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
12458c2ecf20Sopenharmony_ci	return 0;
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_cistatic int vidioc_s_jpegcomp(struct file *file, void *priv,
12498c2ecf20Sopenharmony_ci			 const struct v4l2_jpegcompression *jc)
12508c2ecf20Sopenharmony_ci{
12518c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	if (jc->quality < 0 || jc->quality > 100)
12548c2ecf20Sopenharmony_ci		return -EINVAL;
12558c2ecf20Sopenharmony_ci	v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality);
12568c2ecf20Sopenharmony_ci	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
12578c2ecf20Sopenharmony_ci	return 0;
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic int vidioc_g_parm(struct file *file, void *priv,
12618c2ecf20Sopenharmony_ci			 struct v4l2_streamparm *sp)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	__u32 def_num, def_dem;
12648c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
12678c2ecf20Sopenharmony_ci		return -EINVAL;
12688c2ecf20Sopenharmony_ci	sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
12698c2ecf20Sopenharmony_ci	sp->parm.capture.capturemode = vc->cap_parm.capturemode;
12708c2ecf20Sopenharmony_ci	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
12718c2ecf20Sopenharmony_ci	def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000;
12728c2ecf20Sopenharmony_ci	def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000;
12738c2ecf20Sopenharmony_ci	sp->parm.capture.timeperframe.denominator = def_dem;
12748c2ecf20Sopenharmony_ci	switch (vc->mode.fdec) {
12758c2ecf20Sopenharmony_ci	default:
12768c2ecf20Sopenharmony_ci	case FDEC_1:
12778c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num;
12788c2ecf20Sopenharmony_ci		break;
12798c2ecf20Sopenharmony_ci	case FDEC_2:
12808c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 2;
12818c2ecf20Sopenharmony_ci		break;
12828c2ecf20Sopenharmony_ci	case FDEC_3:
12838c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 3;
12848c2ecf20Sopenharmony_ci		break;
12858c2ecf20Sopenharmony_ci	case FDEC_5:
12868c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 5;
12878c2ecf20Sopenharmony_ci		break;
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n",
12908c2ecf20Sopenharmony_ci		__func__,
12918c2ecf20Sopenharmony_ci		sp->parm.capture.capturemode,
12928c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator,
12938c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.denominator);
12948c2ecf20Sopenharmony_ci	return 0;
12958c2ecf20Sopenharmony_ci}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cistatic int vidioc_s_parm(struct file *file, void *priv,
12988c2ecf20Sopenharmony_ci			 struct v4l2_streamparm *sp)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
13018c2ecf20Sopenharmony_ci	struct s2255_mode mode;
13028c2ecf20Sopenharmony_ci	int fdec = FDEC_1;
13038c2ecf20Sopenharmony_ci	__u32 def_num, def_dem;
13048c2ecf20Sopenharmony_ci	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
13058c2ecf20Sopenharmony_ci		return -EINVAL;
13068c2ecf20Sopenharmony_ci	mode = vc->mode;
13078c2ecf20Sopenharmony_ci	/* high quality capture mode requires a stream restart */
13088c2ecf20Sopenharmony_ci	if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode)
13098c2ecf20Sopenharmony_ci	    && vb2_is_streaming(&vc->vb_vidq))
13108c2ecf20Sopenharmony_ci		return -EBUSY;
13118c2ecf20Sopenharmony_ci	def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
13128c2ecf20Sopenharmony_ci	def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
13138c2ecf20Sopenharmony_ci	if (def_dem != sp->parm.capture.timeperframe.denominator)
13148c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num;
13158c2ecf20Sopenharmony_ci	else if (sp->parm.capture.timeperframe.numerator <= def_num)
13168c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num;
13178c2ecf20Sopenharmony_ci	else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
13188c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 2;
13198c2ecf20Sopenharmony_ci		fdec = FDEC_2;
13208c2ecf20Sopenharmony_ci	} else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
13218c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 3;
13228c2ecf20Sopenharmony_ci		fdec = FDEC_3;
13238c2ecf20Sopenharmony_ci	} else {
13248c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 5;
13258c2ecf20Sopenharmony_ci		fdec = FDEC_5;
13268c2ecf20Sopenharmony_ci	}
13278c2ecf20Sopenharmony_ci	mode.fdec = fdec;
13288c2ecf20Sopenharmony_ci	sp->parm.capture.timeperframe.denominator = def_dem;
13298c2ecf20Sopenharmony_ci	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
13308c2ecf20Sopenharmony_ci	s2255_set_mode(vc, &mode);
13318c2ecf20Sopenharmony_ci	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
13328c2ecf20Sopenharmony_ci		__func__,
13338c2ecf20Sopenharmony_ci		sp->parm.capture.capturemode,
13348c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.numerator,
13358c2ecf20Sopenharmony_ci		sp->parm.capture.timeperframe.denominator, fdec);
13368c2ecf20Sopenharmony_ci	return 0;
13378c2ecf20Sopenharmony_ci}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci#define NUM_SIZE_ENUMS 3
13408c2ecf20Sopenharmony_cistatic const struct v4l2_frmsize_discrete ntsc_sizes[] = {
13418c2ecf20Sopenharmony_ci	{ 640, 480 },
13428c2ecf20Sopenharmony_ci	{ 640, 240 },
13438c2ecf20Sopenharmony_ci	{ 320, 240 },
13448c2ecf20Sopenharmony_ci};
13458c2ecf20Sopenharmony_cistatic const struct v4l2_frmsize_discrete pal_sizes[] = {
13468c2ecf20Sopenharmony_ci	{ 704, 576 },
13478c2ecf20Sopenharmony_ci	{ 704, 288 },
13488c2ecf20Sopenharmony_ci	{ 352, 288 },
13498c2ecf20Sopenharmony_ci};
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cistatic int vidioc_enum_framesizes(struct file *file, void *priv,
13528c2ecf20Sopenharmony_ci			    struct v4l2_frmsizeenum *fe)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
13558c2ecf20Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
13568c2ecf20Sopenharmony_ci	const struct s2255_fmt *fmt;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	if (fe->index >= NUM_SIZE_ENUMS)
13598c2ecf20Sopenharmony_ci		return -EINVAL;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	fmt = format_by_fourcc(fe->pixel_format);
13628c2ecf20Sopenharmony_ci	if (fmt == NULL)
13638c2ecf20Sopenharmony_ci		return -EINVAL;
13648c2ecf20Sopenharmony_ci	fe->type = V4L2_FRMSIZE_TYPE_DISCRETE;
13658c2ecf20Sopenharmony_ci	fe->discrete = is_ntsc ?  ntsc_sizes[fe->index] : pal_sizes[fe->index];
13668c2ecf20Sopenharmony_ci	return 0;
13678c2ecf20Sopenharmony_ci}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_cistatic int vidioc_enum_frameintervals(struct file *file, void *priv,
13708c2ecf20Sopenharmony_ci			    struct v4l2_frmivalenum *fe)
13718c2ecf20Sopenharmony_ci{
13728c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
13738c2ecf20Sopenharmony_ci	const struct s2255_fmt *fmt;
13748c2ecf20Sopenharmony_ci	const struct v4l2_frmsize_discrete *sizes;
13758c2ecf20Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
13768c2ecf20Sopenharmony_ci#define NUM_FRAME_ENUMS 4
13778c2ecf20Sopenharmony_ci	int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
13788c2ecf20Sopenharmony_ci	int i;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	if (fe->index >= NUM_FRAME_ENUMS)
13818c2ecf20Sopenharmony_ci		return -EINVAL;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	fmt = format_by_fourcc(fe->pixel_format);
13848c2ecf20Sopenharmony_ci	if (fmt == NULL)
13858c2ecf20Sopenharmony_ci		return -EINVAL;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	sizes = is_ntsc ? ntsc_sizes : pal_sizes;
13888c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++)
13898c2ecf20Sopenharmony_ci		if (fe->width == sizes->width &&
13908c2ecf20Sopenharmony_ci		    fe->height == sizes->height)
13918c2ecf20Sopenharmony_ci			break;
13928c2ecf20Sopenharmony_ci	if (i == NUM_SIZE_ENUMS)
13938c2ecf20Sopenharmony_ci		return -EINVAL;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
13968c2ecf20Sopenharmony_ci	fe->discrete.denominator = is_ntsc ? 30000 : 25000;
13978c2ecf20Sopenharmony_ci	fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
13988c2ecf20Sopenharmony_ci	dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__,
13998c2ecf20Sopenharmony_ci		fe->discrete.numerator,
14008c2ecf20Sopenharmony_ci		fe->discrete.denominator);
14018c2ecf20Sopenharmony_ci	return 0;
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_cistatic int s2255_open(struct file *file)
14058c2ecf20Sopenharmony_ci{
14068c2ecf20Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
14078c2ecf20Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
14088c2ecf20Sopenharmony_ci	int state;
14098c2ecf20Sopenharmony_ci	int rc = 0;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	rc = v4l2_fh_open(file);
14128c2ecf20Sopenharmony_ci	if (rc != 0)
14138c2ecf20Sopenharmony_ci		return rc;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	dprintk(dev, 1, "s2255: %s\n", __func__);
14168c2ecf20Sopenharmony_ci	state = atomic_read(&dev->fw_data->fw_state);
14178c2ecf20Sopenharmony_ci	switch (state) {
14188c2ecf20Sopenharmony_ci	case S2255_FW_DISCONNECTING:
14198c2ecf20Sopenharmony_ci		return -ENODEV;
14208c2ecf20Sopenharmony_ci	case S2255_FW_FAILED:
14218c2ecf20Sopenharmony_ci		s2255_dev_err(&dev->udev->dev,
14228c2ecf20Sopenharmony_ci			"firmware load failed. retrying.\n");
14238c2ecf20Sopenharmony_ci		s2255_fwload_start(dev);
14248c2ecf20Sopenharmony_ci		wait_event_timeout(dev->fw_data->wait_fw,
14258c2ecf20Sopenharmony_ci				   ((atomic_read(&dev->fw_data->fw_state)
14268c2ecf20Sopenharmony_ci				     == S2255_FW_SUCCESS) ||
14278c2ecf20Sopenharmony_ci				    (atomic_read(&dev->fw_data->fw_state)
14288c2ecf20Sopenharmony_ci				     == S2255_FW_DISCONNECTING)),
14298c2ecf20Sopenharmony_ci				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
14308c2ecf20Sopenharmony_ci		/* state may have changed, re-read */
14318c2ecf20Sopenharmony_ci		state = atomic_read(&dev->fw_data->fw_state);
14328c2ecf20Sopenharmony_ci		break;
14338c2ecf20Sopenharmony_ci	case S2255_FW_NOTLOADED:
14348c2ecf20Sopenharmony_ci	case S2255_FW_LOADED_DSPWAIT:
14358c2ecf20Sopenharmony_ci		/* give S2255_LOAD_TIMEOUT time for firmware to load in case
14368c2ecf20Sopenharmony_ci		   driver loaded and then device immediately opened */
14378c2ecf20Sopenharmony_ci		pr_info("%s waiting for firmware load\n", __func__);
14388c2ecf20Sopenharmony_ci		wait_event_timeout(dev->fw_data->wait_fw,
14398c2ecf20Sopenharmony_ci				   ((atomic_read(&dev->fw_data->fw_state)
14408c2ecf20Sopenharmony_ci				     == S2255_FW_SUCCESS) ||
14418c2ecf20Sopenharmony_ci				    (atomic_read(&dev->fw_data->fw_state)
14428c2ecf20Sopenharmony_ci				     == S2255_FW_DISCONNECTING)),
14438c2ecf20Sopenharmony_ci				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
14448c2ecf20Sopenharmony_ci		/* state may have changed, re-read */
14458c2ecf20Sopenharmony_ci		state = atomic_read(&dev->fw_data->fw_state);
14468c2ecf20Sopenharmony_ci		break;
14478c2ecf20Sopenharmony_ci	case S2255_FW_SUCCESS:
14488c2ecf20Sopenharmony_ci	default:
14498c2ecf20Sopenharmony_ci		break;
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci	/* state may have changed in above switch statement */
14528c2ecf20Sopenharmony_ci	switch (state) {
14538c2ecf20Sopenharmony_ci	case S2255_FW_SUCCESS:
14548c2ecf20Sopenharmony_ci		break;
14558c2ecf20Sopenharmony_ci	case S2255_FW_FAILED:
14568c2ecf20Sopenharmony_ci		pr_info("2255 firmware load failed.\n");
14578c2ecf20Sopenharmony_ci		return -ENODEV;
14588c2ecf20Sopenharmony_ci	case S2255_FW_DISCONNECTING:
14598c2ecf20Sopenharmony_ci		pr_info("%s: disconnecting\n", __func__);
14608c2ecf20Sopenharmony_ci		return -ENODEV;
14618c2ecf20Sopenharmony_ci	case S2255_FW_LOADED_DSPWAIT:
14628c2ecf20Sopenharmony_ci	case S2255_FW_NOTLOADED:
14638c2ecf20Sopenharmony_ci		pr_info("%s: firmware not loaded, please retry\n",
14648c2ecf20Sopenharmony_ci			__func__);
14658c2ecf20Sopenharmony_ci		/*
14668c2ecf20Sopenharmony_ci		 * Timeout on firmware load means device unusable.
14678c2ecf20Sopenharmony_ci		 * Set firmware failure state.
14688c2ecf20Sopenharmony_ci		 * On next s2255_open the firmware will be reloaded.
14698c2ecf20Sopenharmony_ci		 */
14708c2ecf20Sopenharmony_ci		atomic_set(&dev->fw_data->fw_state,
14718c2ecf20Sopenharmony_ci			   S2255_FW_FAILED);
14728c2ecf20Sopenharmony_ci		return -EAGAIN;
14738c2ecf20Sopenharmony_ci	default:
14748c2ecf20Sopenharmony_ci		pr_info("%s: unknown state\n", __func__);
14758c2ecf20Sopenharmony_ci		return -EFAULT;
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci	if (!vc->configured) {
14788c2ecf20Sopenharmony_ci		/* configure channel to default state */
14798c2ecf20Sopenharmony_ci		vc->fmt = &formats[0];
14808c2ecf20Sopenharmony_ci		s2255_set_mode(vc, &vc->mode);
14818c2ecf20Sopenharmony_ci		vc->configured = 1;
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci	return 0;
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_cistatic void s2255_destroy(struct s2255_dev *dev)
14878c2ecf20Sopenharmony_ci{
14888c2ecf20Sopenharmony_ci	dprintk(dev, 1, "%s", __func__);
14898c2ecf20Sopenharmony_ci	/* board shutdown stops the read pipe if it is running */
14908c2ecf20Sopenharmony_ci	s2255_board_shutdown(dev);
14918c2ecf20Sopenharmony_ci	/* make sure firmware still not trying to load */
14928c2ecf20Sopenharmony_ci	del_timer_sync(&dev->timer);  /* only started in .probe and .open */
14938c2ecf20Sopenharmony_ci	if (dev->fw_data->fw_urb) {
14948c2ecf20Sopenharmony_ci		usb_kill_urb(dev->fw_data->fw_urb);
14958c2ecf20Sopenharmony_ci		usb_free_urb(dev->fw_data->fw_urb);
14968c2ecf20Sopenharmony_ci		dev->fw_data->fw_urb = NULL;
14978c2ecf20Sopenharmony_ci	}
14988c2ecf20Sopenharmony_ci	release_firmware(dev->fw_data->fw);
14998c2ecf20Sopenharmony_ci	kfree(dev->fw_data->pfw_data);
15008c2ecf20Sopenharmony_ci	kfree(dev->fw_data);
15018c2ecf20Sopenharmony_ci	/* reset the DSP so firmware can be reloaded next time */
15028c2ecf20Sopenharmony_ci	s2255_reset_dsppower(dev);
15038c2ecf20Sopenharmony_ci	mutex_destroy(&dev->lock);
15048c2ecf20Sopenharmony_ci	usb_put_dev(dev->udev);
15058c2ecf20Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
15068c2ecf20Sopenharmony_ci	kfree(dev->cmdbuf);
15078c2ecf20Sopenharmony_ci	kfree(dev);
15088c2ecf20Sopenharmony_ci}
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations s2255_fops_v4l = {
15118c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
15128c2ecf20Sopenharmony_ci	.open = s2255_open,
15138c2ecf20Sopenharmony_ci	.release = vb2_fop_release,
15148c2ecf20Sopenharmony_ci	.poll = vb2_fop_poll,
15158c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,	/* V4L2 ioctl handler */
15168c2ecf20Sopenharmony_ci	.mmap = vb2_fop_mmap,
15178c2ecf20Sopenharmony_ci	.read = vb2_fop_read,
15188c2ecf20Sopenharmony_ci};
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops s2255_ioctl_ops = {
15218c2ecf20Sopenharmony_ci	.vidioc_querycap = vidioc_querycap,
15228c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
15238c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
15248c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
15258c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
15268c2ecf20Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
15278c2ecf20Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
15288c2ecf20Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
15298c2ecf20Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
15308c2ecf20Sopenharmony_ci	.vidioc_s_std = vidioc_s_std,
15318c2ecf20Sopenharmony_ci	.vidioc_g_std = vidioc_g_std,
15328c2ecf20Sopenharmony_ci	.vidioc_enum_input = vidioc_enum_input,
15338c2ecf20Sopenharmony_ci	.vidioc_g_input = vidioc_g_input,
15348c2ecf20Sopenharmony_ci	.vidioc_s_input = vidioc_s_input,
15358c2ecf20Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
15368c2ecf20Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
15378c2ecf20Sopenharmony_ci	.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
15388c2ecf20Sopenharmony_ci	.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
15398c2ecf20Sopenharmony_ci	.vidioc_s_parm = vidioc_s_parm,
15408c2ecf20Sopenharmony_ci	.vidioc_g_parm = vidioc_g_parm,
15418c2ecf20Sopenharmony_ci	.vidioc_enum_framesizes = vidioc_enum_framesizes,
15428c2ecf20Sopenharmony_ci	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
15438c2ecf20Sopenharmony_ci	.vidioc_log_status  = v4l2_ctrl_log_status,
15448c2ecf20Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
15458c2ecf20Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
15468c2ecf20Sopenharmony_ci};
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_cistatic void s2255_video_device_release(struct video_device *vdev)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
15518c2ecf20Sopenharmony_ci	struct s2255_vc *vc =
15528c2ecf20Sopenharmony_ci		container_of(vdev, struct s2255_vc, vdev);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	dprintk(dev, 4, "%s, chnls: %d\n", __func__,
15558c2ecf20Sopenharmony_ci		atomic_read(&dev->num_channels));
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&vc->hdl);
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&dev->num_channels))
15608c2ecf20Sopenharmony_ci		s2255_destroy(dev);
15618c2ecf20Sopenharmony_ci	return;
15628c2ecf20Sopenharmony_ci}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_cistatic const struct video_device template = {
15658c2ecf20Sopenharmony_ci	.name = "s2255v",
15668c2ecf20Sopenharmony_ci	.fops = &s2255_fops_v4l,
15678c2ecf20Sopenharmony_ci	.ioctl_ops = &s2255_ioctl_ops,
15688c2ecf20Sopenharmony_ci	.release = s2255_video_device_release,
15698c2ecf20Sopenharmony_ci	.tvnorms = S2255_NORMS,
15708c2ecf20Sopenharmony_ci};
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s2255_ctrl_ops = {
15738c2ecf20Sopenharmony_ci	.s_ctrl = s2255_s_ctrl,
15748c2ecf20Sopenharmony_ci};
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config color_filter_ctrl = {
15778c2ecf20Sopenharmony_ci	.ops = &s2255_ctrl_ops,
15788c2ecf20Sopenharmony_ci	.name = "Color Filter",
15798c2ecf20Sopenharmony_ci	.id = V4L2_CID_S2255_COLORFILTER,
15808c2ecf20Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
15818c2ecf20Sopenharmony_ci	.max = 1,
15828c2ecf20Sopenharmony_ci	.step = 1,
15838c2ecf20Sopenharmony_ci	.def = 1,
15848c2ecf20Sopenharmony_ci};
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_cistatic int s2255_probe_v4l(struct s2255_dev *dev)
15878c2ecf20Sopenharmony_ci{
15888c2ecf20Sopenharmony_ci	int ret;
15898c2ecf20Sopenharmony_ci	int i;
15908c2ecf20Sopenharmony_ci	int cur_nr = video_nr;
15918c2ecf20Sopenharmony_ci	struct s2255_vc *vc;
15928c2ecf20Sopenharmony_ci	struct vb2_queue *q;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
15958c2ecf20Sopenharmony_ci	if (ret)
15968c2ecf20Sopenharmony_ci		return ret;
15978c2ecf20Sopenharmony_ci	/* initialize all video 4 linux */
15988c2ecf20Sopenharmony_ci	/* register 4 video devices */
15998c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
16008c2ecf20Sopenharmony_ci		vc = &dev->vc[i];
16018c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&vc->buf_list);
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_init(&vc->hdl, 6);
16048c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
16058c2ecf20Sopenharmony_ci				V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT);
16068c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
16078c2ecf20Sopenharmony_ci				V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST);
16088c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
16098c2ecf20Sopenharmony_ci				V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION);
16108c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
16118c2ecf20Sopenharmony_ci				V4L2_CID_HUE, 0, 255, 1, DEF_HUE);
16128c2ecf20Sopenharmony_ci		vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl,
16138c2ecf20Sopenharmony_ci				&s2255_ctrl_ops,
16148c2ecf20Sopenharmony_ci				V4L2_CID_JPEG_COMPRESSION_QUALITY,
16158c2ecf20Sopenharmony_ci				0, 100, 1, S2255_DEF_JPEG_QUAL);
16168c2ecf20Sopenharmony_ci		if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER &&
16178c2ecf20Sopenharmony_ci		    (dev->pid != 0x2257 || vc->idx <= 1))
16188c2ecf20Sopenharmony_ci			v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl,
16198c2ecf20Sopenharmony_ci					     NULL);
16208c2ecf20Sopenharmony_ci		if (vc->hdl.error) {
16218c2ecf20Sopenharmony_ci			ret = vc->hdl.error;
16228c2ecf20Sopenharmony_ci			v4l2_ctrl_handler_free(&vc->hdl);
16238c2ecf20Sopenharmony_ci			dev_err(&dev->udev->dev, "couldn't register control\n");
16248c2ecf20Sopenharmony_ci			break;
16258c2ecf20Sopenharmony_ci		}
16268c2ecf20Sopenharmony_ci		q = &vc->vb_vidq;
16278c2ecf20Sopenharmony_ci		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
16288c2ecf20Sopenharmony_ci		q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR;
16298c2ecf20Sopenharmony_ci		q->drv_priv = vc;
16308c2ecf20Sopenharmony_ci		q->lock = &vc->vb_lock;
16318c2ecf20Sopenharmony_ci		q->buf_struct_size = sizeof(struct s2255_buffer);
16328c2ecf20Sopenharmony_ci		q->mem_ops = &vb2_vmalloc_memops;
16338c2ecf20Sopenharmony_ci		q->ops = &s2255_video_qops;
16348c2ecf20Sopenharmony_ci		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
16358c2ecf20Sopenharmony_ci		ret = vb2_queue_init(q);
16368c2ecf20Sopenharmony_ci		if (ret != 0) {
16378c2ecf20Sopenharmony_ci			dev_err(&dev->udev->dev,
16388c2ecf20Sopenharmony_ci				"%s vb2_queue_init 0x%x\n", __func__, ret);
16398c2ecf20Sopenharmony_ci			break;
16408c2ecf20Sopenharmony_ci		}
16418c2ecf20Sopenharmony_ci		/* register video devices */
16428c2ecf20Sopenharmony_ci		vc->vdev = template;
16438c2ecf20Sopenharmony_ci		vc->vdev.queue = q;
16448c2ecf20Sopenharmony_ci		vc->vdev.ctrl_handler = &vc->hdl;
16458c2ecf20Sopenharmony_ci		vc->vdev.lock = &dev->lock;
16468c2ecf20Sopenharmony_ci		vc->vdev.v4l2_dev = &dev->v4l2_dev;
16478c2ecf20Sopenharmony_ci		vc->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
16488c2ecf20Sopenharmony_ci				       V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
16498c2ecf20Sopenharmony_ci		video_set_drvdata(&vc->vdev, vc);
16508c2ecf20Sopenharmony_ci		if (video_nr == -1)
16518c2ecf20Sopenharmony_ci			ret = video_register_device(&vc->vdev,
16528c2ecf20Sopenharmony_ci						    VFL_TYPE_VIDEO,
16538c2ecf20Sopenharmony_ci						    video_nr);
16548c2ecf20Sopenharmony_ci		else
16558c2ecf20Sopenharmony_ci			ret = video_register_device(&vc->vdev,
16568c2ecf20Sopenharmony_ci						    VFL_TYPE_VIDEO,
16578c2ecf20Sopenharmony_ci						    cur_nr + i);
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci		if (ret) {
16608c2ecf20Sopenharmony_ci			dev_err(&dev->udev->dev,
16618c2ecf20Sopenharmony_ci				"failed to register video device!\n");
16628c2ecf20Sopenharmony_ci			break;
16638c2ecf20Sopenharmony_ci		}
16648c2ecf20Sopenharmony_ci		atomic_inc(&dev->num_channels);
16658c2ecf20Sopenharmony_ci		v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
16668c2ecf20Sopenharmony_ci			  video_device_node_name(&vc->vdev));
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	}
16698c2ecf20Sopenharmony_ci	pr_info("Sensoray 2255 V4L driver Revision: %s\n",
16708c2ecf20Sopenharmony_ci		S2255_VERSION);
16718c2ecf20Sopenharmony_ci	/* if no channels registered, return error and probe will fail*/
16728c2ecf20Sopenharmony_ci	if (atomic_read(&dev->num_channels) == 0) {
16738c2ecf20Sopenharmony_ci		v4l2_device_unregister(&dev->v4l2_dev);
16748c2ecf20Sopenharmony_ci		return ret;
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci	if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
16778c2ecf20Sopenharmony_ci		pr_warn("s2255: Not all channels available.\n");
16788c2ecf20Sopenharmony_ci	return 0;
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci/* this function moves the usb stream read pipe data
16828c2ecf20Sopenharmony_ci * into the system buffers.
16838c2ecf20Sopenharmony_ci * returns 0 on success, EAGAIN if more data to process( call this
16848c2ecf20Sopenharmony_ci * function again).
16858c2ecf20Sopenharmony_ci *
16868c2ecf20Sopenharmony_ci * Received frame structure:
16878c2ecf20Sopenharmony_ci * bytes 0-3:  marker : 0x2255DA4AL (S2255_MARKER_FRAME)
16888c2ecf20Sopenharmony_ci * bytes 4-7:  channel: 0-3
16898c2ecf20Sopenharmony_ci * bytes 8-11: payload size:  size of the frame
16908c2ecf20Sopenharmony_ci * bytes 12-payloadsize+12:  frame data
16918c2ecf20Sopenharmony_ci */
16928c2ecf20Sopenharmony_cistatic int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	char *pdest;
16958c2ecf20Sopenharmony_ci	u32 offset = 0;
16968c2ecf20Sopenharmony_ci	int bframe = 0;
16978c2ecf20Sopenharmony_ci	char *psrc;
16988c2ecf20Sopenharmony_ci	unsigned long copy_size;
16998c2ecf20Sopenharmony_ci	unsigned long size;
17008c2ecf20Sopenharmony_ci	s32 idx = -1;
17018c2ecf20Sopenharmony_ci	struct s2255_framei *frm;
17028c2ecf20Sopenharmony_ci	unsigned char *pdata;
17038c2ecf20Sopenharmony_ci	struct s2255_vc *vc;
17048c2ecf20Sopenharmony_ci	dprintk(dev, 100, "buffer to user\n");
17058c2ecf20Sopenharmony_ci	vc = &dev->vc[dev->cc];
17068c2ecf20Sopenharmony_ci	idx = vc->cur_frame;
17078c2ecf20Sopenharmony_ci	frm = &vc->buffer.frame[idx];
17088c2ecf20Sopenharmony_ci	if (frm->ulState == S2255_READ_IDLE) {
17098c2ecf20Sopenharmony_ci		int jj;
17108c2ecf20Sopenharmony_ci		unsigned int cc;
17118c2ecf20Sopenharmony_ci		__le32 *pdword; /*data from dsp is little endian */
17128c2ecf20Sopenharmony_ci		int payload;
17138c2ecf20Sopenharmony_ci		/* search for marker codes */
17148c2ecf20Sopenharmony_ci		pdata = (unsigned char *)pipe_info->transfer_buffer;
17158c2ecf20Sopenharmony_ci		pdword = (__le32 *)pdata;
17168c2ecf20Sopenharmony_ci		for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
17178c2ecf20Sopenharmony_ci			switch (*pdword) {
17188c2ecf20Sopenharmony_ci			case S2255_MARKER_FRAME:
17198c2ecf20Sopenharmony_ci				dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
17208c2ecf20Sopenharmony_ci					jj, pdata[0], pdata[1]);
17218c2ecf20Sopenharmony_ci				offset = jj + PREFIX_SIZE;
17228c2ecf20Sopenharmony_ci				bframe = 1;
17238c2ecf20Sopenharmony_ci				cc = le32_to_cpu(pdword[1]);
17248c2ecf20Sopenharmony_ci				if (cc >= MAX_CHANNELS) {
17258c2ecf20Sopenharmony_ci					dprintk(dev, 0,
17268c2ecf20Sopenharmony_ci						"bad channel\n");
17278c2ecf20Sopenharmony_ci					return -EINVAL;
17288c2ecf20Sopenharmony_ci				}
17298c2ecf20Sopenharmony_ci				/* reverse it */
17308c2ecf20Sopenharmony_ci				dev->cc = G_chnmap[cc];
17318c2ecf20Sopenharmony_ci				vc = &dev->vc[dev->cc];
17328c2ecf20Sopenharmony_ci				payload =  le32_to_cpu(pdword[3]);
17338c2ecf20Sopenharmony_ci				if (payload > vc->req_image_size) {
17348c2ecf20Sopenharmony_ci					vc->bad_payload++;
17358c2ecf20Sopenharmony_ci					/* discard the bad frame */
17368c2ecf20Sopenharmony_ci					return -EINVAL;
17378c2ecf20Sopenharmony_ci				}
17388c2ecf20Sopenharmony_ci				vc->pkt_size = payload;
17398c2ecf20Sopenharmony_ci				vc->jpg_size = le32_to_cpu(pdword[4]);
17408c2ecf20Sopenharmony_ci				break;
17418c2ecf20Sopenharmony_ci			case S2255_MARKER_RESPONSE:
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci				pdata += DEF_USB_BLOCK;
17448c2ecf20Sopenharmony_ci				jj += DEF_USB_BLOCK;
17458c2ecf20Sopenharmony_ci				if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
17468c2ecf20Sopenharmony_ci					break;
17478c2ecf20Sopenharmony_ci				cc = G_chnmap[le32_to_cpu(pdword[1])];
17488c2ecf20Sopenharmony_ci				if (cc >= MAX_CHANNELS)
17498c2ecf20Sopenharmony_ci					break;
17508c2ecf20Sopenharmony_ci				vc = &dev->vc[cc];
17518c2ecf20Sopenharmony_ci				switch (pdword[2]) {
17528c2ecf20Sopenharmony_ci				case S2255_RESPONSE_SETMODE:
17538c2ecf20Sopenharmony_ci					/* check if channel valid */
17548c2ecf20Sopenharmony_ci					/* set mode ready */
17558c2ecf20Sopenharmony_ci					vc->setmode_ready = 1;
17568c2ecf20Sopenharmony_ci					wake_up(&vc->wait_setmode);
17578c2ecf20Sopenharmony_ci					dprintk(dev, 5, "setmode rdy %d\n", cc);
17588c2ecf20Sopenharmony_ci					break;
17598c2ecf20Sopenharmony_ci				case S2255_RESPONSE_FW:
17608c2ecf20Sopenharmony_ci					dev->chn_ready |= (1 << cc);
17618c2ecf20Sopenharmony_ci					if ((dev->chn_ready & 0x0f) != 0x0f)
17628c2ecf20Sopenharmony_ci						break;
17638c2ecf20Sopenharmony_ci					/* all channels ready */
17648c2ecf20Sopenharmony_ci					pr_info("s2255: fw loaded\n");
17658c2ecf20Sopenharmony_ci					atomic_set(&dev->fw_data->fw_state,
17668c2ecf20Sopenharmony_ci						   S2255_FW_SUCCESS);
17678c2ecf20Sopenharmony_ci					wake_up(&dev->fw_data->wait_fw);
17688c2ecf20Sopenharmony_ci					break;
17698c2ecf20Sopenharmony_ci				case S2255_RESPONSE_STATUS:
17708c2ecf20Sopenharmony_ci					vc->vidstatus = le32_to_cpu(pdword[3]);
17718c2ecf20Sopenharmony_ci					vc->vidstatus_ready = 1;
17728c2ecf20Sopenharmony_ci					wake_up(&vc->wait_vidstatus);
17738c2ecf20Sopenharmony_ci					dprintk(dev, 5, "vstat %x chan %d\n",
17748c2ecf20Sopenharmony_ci						le32_to_cpu(pdword[3]), cc);
17758c2ecf20Sopenharmony_ci					break;
17768c2ecf20Sopenharmony_ci				default:
17778c2ecf20Sopenharmony_ci					pr_info("s2255 unknown resp\n");
17788c2ecf20Sopenharmony_ci				}
17798c2ecf20Sopenharmony_ci				pdata++;
17808c2ecf20Sopenharmony_ci				break;
17818c2ecf20Sopenharmony_ci			default:
17828c2ecf20Sopenharmony_ci				pdata++;
17838c2ecf20Sopenharmony_ci				break;
17848c2ecf20Sopenharmony_ci			}
17858c2ecf20Sopenharmony_ci			if (bframe)
17868c2ecf20Sopenharmony_ci				break;
17878c2ecf20Sopenharmony_ci		} /* for */
17888c2ecf20Sopenharmony_ci		if (!bframe)
17898c2ecf20Sopenharmony_ci			return -EINVAL;
17908c2ecf20Sopenharmony_ci	}
17918c2ecf20Sopenharmony_ci	vc = &dev->vc[dev->cc];
17928c2ecf20Sopenharmony_ci	idx = vc->cur_frame;
17938c2ecf20Sopenharmony_ci	frm = &vc->buffer.frame[idx];
17948c2ecf20Sopenharmony_ci	/* search done.  now find out if should be acquiring on this channel */
17958c2ecf20Sopenharmony_ci	if (!vb2_is_streaming(&vc->vb_vidq)) {
17968c2ecf20Sopenharmony_ci		/* we found a frame, but this channel is turned off */
17978c2ecf20Sopenharmony_ci		frm->ulState = S2255_READ_IDLE;
17988c2ecf20Sopenharmony_ci		return -EINVAL;
17998c2ecf20Sopenharmony_ci	}
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	if (frm->ulState == S2255_READ_IDLE) {
18028c2ecf20Sopenharmony_ci		frm->ulState = S2255_READ_FRAME;
18038c2ecf20Sopenharmony_ci		frm->cur_size = 0;
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	/* skip the marker 512 bytes (and offset if out of sync) */
18078c2ecf20Sopenharmony_ci	psrc = (u8 *)pipe_info->transfer_buffer + offset;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	if (frm->lpvbits == NULL) {
18118c2ecf20Sopenharmony_ci		dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d",
18128c2ecf20Sopenharmony_ci			frm, dev, dev->cc, idx);
18138c2ecf20Sopenharmony_ci		return -ENOMEM;
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	pdest = frm->lpvbits + frm->cur_size;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	copy_size = (pipe_info->cur_transfer_size - offset);
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	size = vc->pkt_size - PREFIX_SIZE;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	/* sanity check on pdest */
18238c2ecf20Sopenharmony_ci	if ((copy_size + frm->cur_size) < vc->req_image_size)
18248c2ecf20Sopenharmony_ci		memcpy(pdest, psrc, copy_size);
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	frm->cur_size += copy_size;
18278c2ecf20Sopenharmony_ci	dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if (frm->cur_size >= size) {
18308c2ecf20Sopenharmony_ci		dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n",
18318c2ecf20Sopenharmony_ci			dev->cc, idx);
18328c2ecf20Sopenharmony_ci		vc->last_frame = vc->cur_frame;
18338c2ecf20Sopenharmony_ci		vc->cur_frame++;
18348c2ecf20Sopenharmony_ci		/* end of system frame ring buffer, start at zero */
18358c2ecf20Sopenharmony_ci		if ((vc->cur_frame == SYS_FRAMES) ||
18368c2ecf20Sopenharmony_ci		    (vc->cur_frame == vc->buffer.dwFrames))
18378c2ecf20Sopenharmony_ci			vc->cur_frame = 0;
18388c2ecf20Sopenharmony_ci		/* frame ready */
18398c2ecf20Sopenharmony_ci		if (vb2_is_streaming(&vc->vb_vidq))
18408c2ecf20Sopenharmony_ci			s2255_got_frame(vc, vc->jpg_size);
18418c2ecf20Sopenharmony_ci		vc->frame_count++;
18428c2ecf20Sopenharmony_ci		frm->ulState = S2255_READ_IDLE;
18438c2ecf20Sopenharmony_ci		frm->cur_size = 0;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	}
18468c2ecf20Sopenharmony_ci	/* done successfully */
18478c2ecf20Sopenharmony_ci	return 0;
18488c2ecf20Sopenharmony_ci}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_cistatic void s2255_read_video_callback(struct s2255_dev *dev,
18518c2ecf20Sopenharmony_ci				      struct s2255_pipeinfo *pipe_info)
18528c2ecf20Sopenharmony_ci{
18538c2ecf20Sopenharmony_ci	int res;
18548c2ecf20Sopenharmony_ci	dprintk(dev, 50, "callback read video\n");
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	if (dev->cc >= MAX_CHANNELS) {
18578c2ecf20Sopenharmony_ci		dev->cc = 0;
18588c2ecf20Sopenharmony_ci		dev_err(&dev->udev->dev, "invalid channel\n");
18598c2ecf20Sopenharmony_ci		return;
18608c2ecf20Sopenharmony_ci	}
18618c2ecf20Sopenharmony_ci	/* otherwise copy to the system buffers */
18628c2ecf20Sopenharmony_ci	res = save_frame(dev, pipe_info);
18638c2ecf20Sopenharmony_ci	if (res != 0)
18648c2ecf20Sopenharmony_ci		dprintk(dev, 4, "s2255: read callback failed\n");
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	dprintk(dev, 50, "callback read video done\n");
18678c2ecf20Sopenharmony_ci	return;
18688c2ecf20Sopenharmony_ci}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cistatic long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
18718c2ecf20Sopenharmony_ci			     u16 Index, u16 Value, void *TransferBuffer,
18728c2ecf20Sopenharmony_ci			     s32 TransferBufferLength, int bOut)
18738c2ecf20Sopenharmony_ci{
18748c2ecf20Sopenharmony_ci	int r;
18758c2ecf20Sopenharmony_ci	unsigned char *buf;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	buf = kmalloc(TransferBufferLength, GFP_KERNEL);
18788c2ecf20Sopenharmony_ci	if (!buf)
18798c2ecf20Sopenharmony_ci		return -ENOMEM;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	if (!bOut) {
18828c2ecf20Sopenharmony_ci		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
18838c2ecf20Sopenharmony_ci				    Request,
18848c2ecf20Sopenharmony_ci				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
18858c2ecf20Sopenharmony_ci				    USB_DIR_IN,
18868c2ecf20Sopenharmony_ci				    Value, Index, buf,
18878c2ecf20Sopenharmony_ci				    TransferBufferLength, USB_CTRL_SET_TIMEOUT);
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci		if (r >= 0)
18908c2ecf20Sopenharmony_ci			memcpy(TransferBuffer, buf, TransferBufferLength);
18918c2ecf20Sopenharmony_ci	} else {
18928c2ecf20Sopenharmony_ci		memcpy(buf, TransferBuffer, TransferBufferLength);
18938c2ecf20Sopenharmony_ci		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
18948c2ecf20Sopenharmony_ci				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
18958c2ecf20Sopenharmony_ci				    Value, Index, buf,
18968c2ecf20Sopenharmony_ci				    TransferBufferLength, USB_CTRL_SET_TIMEOUT);
18978c2ecf20Sopenharmony_ci	}
18988c2ecf20Sopenharmony_ci	kfree(buf);
18998c2ecf20Sopenharmony_ci	return r;
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci/*
19038c2ecf20Sopenharmony_ci * retrieve FX2 firmware version. future use.
19048c2ecf20Sopenharmony_ci * @param dev pointer to device extension
19058c2ecf20Sopenharmony_ci * @return -1 for fail, else returns firmware version as an int(16 bits)
19068c2ecf20Sopenharmony_ci */
19078c2ecf20Sopenharmony_cistatic int s2255_get_fx2fw(struct s2255_dev *dev)
19088c2ecf20Sopenharmony_ci{
19098c2ecf20Sopenharmony_ci	int fw;
19108c2ecf20Sopenharmony_ci	int ret;
19118c2ecf20Sopenharmony_ci	unsigned char transBuffer[64];
19128c2ecf20Sopenharmony_ci	ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
19138c2ecf20Sopenharmony_ci			       S2255_VR_IN);
19148c2ecf20Sopenharmony_ci	if (ret < 0)
19158c2ecf20Sopenharmony_ci		dprintk(dev, 2, "get fw error: %x\n", ret);
19168c2ecf20Sopenharmony_ci	fw = transBuffer[0] + (transBuffer[1] << 8);
19178c2ecf20Sopenharmony_ci	dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
19188c2ecf20Sopenharmony_ci	return fw;
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci/*
19228c2ecf20Sopenharmony_ci * Create the system ring buffer to copy frames into from the
19238c2ecf20Sopenharmony_ci * usb read pipe.
19248c2ecf20Sopenharmony_ci */
19258c2ecf20Sopenharmony_cistatic int s2255_create_sys_buffers(struct s2255_vc *vc)
19268c2ecf20Sopenharmony_ci{
19278c2ecf20Sopenharmony_ci	unsigned long i;
19288c2ecf20Sopenharmony_ci	unsigned long reqsize;
19298c2ecf20Sopenharmony_ci	vc->buffer.dwFrames = SYS_FRAMES;
19308c2ecf20Sopenharmony_ci	/* always allocate maximum size(PAL) for system buffers */
19318c2ecf20Sopenharmony_ci	reqsize = SYS_FRAMES_MAXSIZE;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	if (reqsize > SYS_FRAMES_MAXSIZE)
19348c2ecf20Sopenharmony_ci		reqsize = SYS_FRAMES_MAXSIZE;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	for (i = 0; i < SYS_FRAMES; i++) {
19378c2ecf20Sopenharmony_ci		/* allocate the frames */
19388c2ecf20Sopenharmony_ci		vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
19398c2ecf20Sopenharmony_ci		vc->buffer.frame[i].size = reqsize;
19408c2ecf20Sopenharmony_ci		if (vc->buffer.frame[i].lpvbits == NULL) {
19418c2ecf20Sopenharmony_ci			pr_info("out of memory.  using less frames\n");
19428c2ecf20Sopenharmony_ci			vc->buffer.dwFrames = i;
19438c2ecf20Sopenharmony_ci			break;
19448c2ecf20Sopenharmony_ci		}
19458c2ecf20Sopenharmony_ci	}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	/* make sure internal states are set */
19488c2ecf20Sopenharmony_ci	for (i = 0; i < SYS_FRAMES; i++) {
19498c2ecf20Sopenharmony_ci		vc->buffer.frame[i].ulState = 0;
19508c2ecf20Sopenharmony_ci		vc->buffer.frame[i].cur_size = 0;
19518c2ecf20Sopenharmony_ci	}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	vc->cur_frame = 0;
19548c2ecf20Sopenharmony_ci	vc->last_frame = -1;
19558c2ecf20Sopenharmony_ci	return 0;
19568c2ecf20Sopenharmony_ci}
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_cistatic int s2255_release_sys_buffers(struct s2255_vc *vc)
19598c2ecf20Sopenharmony_ci{
19608c2ecf20Sopenharmony_ci	unsigned long i;
19618c2ecf20Sopenharmony_ci	for (i = 0; i < SYS_FRAMES; i++) {
19628c2ecf20Sopenharmony_ci		vfree(vc->buffer.frame[i].lpvbits);
19638c2ecf20Sopenharmony_ci		vc->buffer.frame[i].lpvbits = NULL;
19648c2ecf20Sopenharmony_ci	}
19658c2ecf20Sopenharmony_ci	return 0;
19668c2ecf20Sopenharmony_ci}
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_cistatic int s2255_board_init(struct s2255_dev *dev)
19698c2ecf20Sopenharmony_ci{
19708c2ecf20Sopenharmony_ci	struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
19718c2ecf20Sopenharmony_ci	int fw_ver;
19728c2ecf20Sopenharmony_ci	int j;
19738c2ecf20Sopenharmony_ci	struct s2255_pipeinfo *pipe = &dev->pipe;
19748c2ecf20Sopenharmony_ci	dprintk(dev, 4, "board init: %p", dev);
19758c2ecf20Sopenharmony_ci	memset(pipe, 0, sizeof(*pipe));
19768c2ecf20Sopenharmony_ci	pipe->dev = dev;
19778c2ecf20Sopenharmony_ci	pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
19788c2ecf20Sopenharmony_ci	pipe->max_transfer_size = S2255_USB_XFER_SIZE;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
19818c2ecf20Sopenharmony_ci					GFP_KERNEL);
19828c2ecf20Sopenharmony_ci	if (pipe->transfer_buffer == NULL) {
19838c2ecf20Sopenharmony_ci		dprintk(dev, 1, "out of memory!\n");
19848c2ecf20Sopenharmony_ci		return -ENOMEM;
19858c2ecf20Sopenharmony_ci	}
19868c2ecf20Sopenharmony_ci	/* query the firmware */
19878c2ecf20Sopenharmony_ci	fw_ver = s2255_get_fx2fw(dev);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	pr_info("s2255: usb firmware version %d.%d\n",
19908c2ecf20Sopenharmony_ci		(fw_ver >> 8) & 0xff,
19918c2ecf20Sopenharmony_ci		fw_ver & 0xff);
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	if (fw_ver < S2255_CUR_USB_FWVER)
19948c2ecf20Sopenharmony_ci		pr_info("s2255: newer USB firmware available\n");
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	for (j = 0; j < MAX_CHANNELS; j++) {
19978c2ecf20Sopenharmony_ci		struct s2255_vc *vc = &dev->vc[j];
19988c2ecf20Sopenharmony_ci		vc->mode = mode_def;
19998c2ecf20Sopenharmony_ci		if (dev->pid == 0x2257 && j > 1)
20008c2ecf20Sopenharmony_ci			vc->mode.color |= (1 << 16);
20018c2ecf20Sopenharmony_ci		vc->jpegqual = S2255_DEF_JPEG_QUAL;
20028c2ecf20Sopenharmony_ci		vc->width = LINE_SZ_4CIFS_NTSC;
20038c2ecf20Sopenharmony_ci		vc->height = NUM_LINES_4CIFS_NTSC * 2;
20048c2ecf20Sopenharmony_ci		vc->std = V4L2_STD_NTSC_M;
20058c2ecf20Sopenharmony_ci		vc->fmt = &formats[0];
20068c2ecf20Sopenharmony_ci		vc->mode.restart = 1;
20078c2ecf20Sopenharmony_ci		vc->req_image_size = get_transfer_size(&mode_def);
20088c2ecf20Sopenharmony_ci		vc->frame_count = 0;
20098c2ecf20Sopenharmony_ci		/* create the system buffers */
20108c2ecf20Sopenharmony_ci		s2255_create_sys_buffers(vc);
20118c2ecf20Sopenharmony_ci	}
20128c2ecf20Sopenharmony_ci	/* start read pipe */
20138c2ecf20Sopenharmony_ci	s2255_start_readpipe(dev);
20148c2ecf20Sopenharmony_ci	dprintk(dev, 1, "%s: success\n", __func__);
20158c2ecf20Sopenharmony_ci	return 0;
20168c2ecf20Sopenharmony_ci}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_cistatic int s2255_board_shutdown(struct s2255_dev *dev)
20198c2ecf20Sopenharmony_ci{
20208c2ecf20Sopenharmony_ci	u32 i;
20218c2ecf20Sopenharmony_ci	dprintk(dev, 1, "%s: dev: %p", __func__,  dev);
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
20248c2ecf20Sopenharmony_ci		if (vb2_is_streaming(&dev->vc[i].vb_vidq))
20258c2ecf20Sopenharmony_ci			s2255_stop_acquire(&dev->vc[i]);
20268c2ecf20Sopenharmony_ci	}
20278c2ecf20Sopenharmony_ci	s2255_stop_readpipe(dev);
20288c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++)
20298c2ecf20Sopenharmony_ci		s2255_release_sys_buffers(&dev->vc[i]);
20308c2ecf20Sopenharmony_ci	/* release transfer buffer */
20318c2ecf20Sopenharmony_ci	kfree(dev->pipe.transfer_buffer);
20328c2ecf20Sopenharmony_ci	return 0;
20338c2ecf20Sopenharmony_ci}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_cistatic void read_pipe_completion(struct urb *purb)
20368c2ecf20Sopenharmony_ci{
20378c2ecf20Sopenharmony_ci	struct s2255_pipeinfo *pipe_info;
20388c2ecf20Sopenharmony_ci	struct s2255_dev *dev;
20398c2ecf20Sopenharmony_ci	int status;
20408c2ecf20Sopenharmony_ci	int pipe;
20418c2ecf20Sopenharmony_ci	pipe_info = purb->context;
20428c2ecf20Sopenharmony_ci	if (pipe_info == NULL) {
20438c2ecf20Sopenharmony_ci		dev_err(&purb->dev->dev, "no context!\n");
20448c2ecf20Sopenharmony_ci		return;
20458c2ecf20Sopenharmony_ci	}
20468c2ecf20Sopenharmony_ci	dev = pipe_info->dev;
20478c2ecf20Sopenharmony_ci	if (dev == NULL) {
20488c2ecf20Sopenharmony_ci		dev_err(&purb->dev->dev, "no context!\n");
20498c2ecf20Sopenharmony_ci		return;
20508c2ecf20Sopenharmony_ci	}
20518c2ecf20Sopenharmony_ci	status = purb->status;
20528c2ecf20Sopenharmony_ci	/* if shutting down, do not resubmit, exit immediately */
20538c2ecf20Sopenharmony_ci	if (status == -ESHUTDOWN) {
20548c2ecf20Sopenharmony_ci		dprintk(dev, 2, "%s: err shutdown\n", __func__);
20558c2ecf20Sopenharmony_ci		pipe_info->err_count++;
20568c2ecf20Sopenharmony_ci		return;
20578c2ecf20Sopenharmony_ci	}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	if (pipe_info->state == 0) {
20608c2ecf20Sopenharmony_ci		dprintk(dev, 2, "%s: exiting USB pipe", __func__);
20618c2ecf20Sopenharmony_ci		return;
20628c2ecf20Sopenharmony_ci	}
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	if (status == 0)
20658c2ecf20Sopenharmony_ci		s2255_read_video_callback(dev, pipe_info);
20668c2ecf20Sopenharmony_ci	else {
20678c2ecf20Sopenharmony_ci		pipe_info->err_count++;
20688c2ecf20Sopenharmony_ci		dprintk(dev, 1, "%s: failed URB %d\n", __func__, status);
20698c2ecf20Sopenharmony_ci	}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
20728c2ecf20Sopenharmony_ci	/* reuse urb */
20738c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
20748c2ecf20Sopenharmony_ci			  pipe,
20758c2ecf20Sopenharmony_ci			  pipe_info->transfer_buffer,
20768c2ecf20Sopenharmony_ci			  pipe_info->cur_transfer_size,
20778c2ecf20Sopenharmony_ci			  read_pipe_completion, pipe_info);
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	if (pipe_info->state != 0) {
20808c2ecf20Sopenharmony_ci		if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC))
20818c2ecf20Sopenharmony_ci			dev_err(&dev->udev->dev, "error submitting urb\n");
20828c2ecf20Sopenharmony_ci	} else {
20838c2ecf20Sopenharmony_ci		dprintk(dev, 2, "%s :complete state 0\n", __func__);
20848c2ecf20Sopenharmony_ci	}
20858c2ecf20Sopenharmony_ci	return;
20868c2ecf20Sopenharmony_ci}
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_cistatic int s2255_start_readpipe(struct s2255_dev *dev)
20898c2ecf20Sopenharmony_ci{
20908c2ecf20Sopenharmony_ci	int pipe;
20918c2ecf20Sopenharmony_ci	int retval;
20928c2ecf20Sopenharmony_ci	struct s2255_pipeinfo *pipe_info = &dev->pipe;
20938c2ecf20Sopenharmony_ci	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
20948c2ecf20Sopenharmony_ci	dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint);
20958c2ecf20Sopenharmony_ci	pipe_info->state = 1;
20968c2ecf20Sopenharmony_ci	pipe_info->err_count = 0;
20978c2ecf20Sopenharmony_ci	pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
20988c2ecf20Sopenharmony_ci	if (!pipe_info->stream_urb)
20998c2ecf20Sopenharmony_ci		return -ENOMEM;
21008c2ecf20Sopenharmony_ci	/* transfer buffer allocated in board_init */
21018c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
21028c2ecf20Sopenharmony_ci			  pipe,
21038c2ecf20Sopenharmony_ci			  pipe_info->transfer_buffer,
21048c2ecf20Sopenharmony_ci			  pipe_info->cur_transfer_size,
21058c2ecf20Sopenharmony_ci			  read_pipe_completion, pipe_info);
21068c2ecf20Sopenharmony_ci	retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
21078c2ecf20Sopenharmony_ci	if (retval) {
21088c2ecf20Sopenharmony_ci		pr_err("s2255: start read pipe failed\n");
21098c2ecf20Sopenharmony_ci		return retval;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci	return 0;
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci/* starts acquisition process */
21158c2ecf20Sopenharmony_cistatic int s2255_start_acquire(struct s2255_vc *vc)
21168c2ecf20Sopenharmony_ci{
21178c2ecf20Sopenharmony_ci	int res;
21188c2ecf20Sopenharmony_ci	unsigned long chn_rev;
21198c2ecf20Sopenharmony_ci	int j;
21208c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
21218c2ecf20Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	mutex_lock(&dev->cmdlock);
21248c2ecf20Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
21258c2ecf20Sopenharmony_ci	vc->last_frame = -1;
21268c2ecf20Sopenharmony_ci	vc->bad_payload = 0;
21278c2ecf20Sopenharmony_ci	vc->cur_frame = 0;
21288c2ecf20Sopenharmony_ci	for (j = 0; j < SYS_FRAMES; j++) {
21298c2ecf20Sopenharmony_ci		vc->buffer.frame[j].ulState = 0;
21308c2ecf20Sopenharmony_ci		vc->buffer.frame[j].cur_size = 0;
21318c2ecf20Sopenharmony_ci	}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci	/* send the start command */
21348c2ecf20Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
21358c2ecf20Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
21368c2ecf20Sopenharmony_ci	buffer[2] = CMD_START;
21378c2ecf20Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
21388c2ecf20Sopenharmony_ci	if (res != 0)
21398c2ecf20Sopenharmony_ci		dev_err(&dev->udev->dev, "CMD_START error\n");
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res);
21428c2ecf20Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
21438c2ecf20Sopenharmony_ci	return res;
21448c2ecf20Sopenharmony_ci}
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_cistatic int s2255_stop_acquire(struct s2255_vc *vc)
21478c2ecf20Sopenharmony_ci{
21488c2ecf20Sopenharmony_ci	int res;
21498c2ecf20Sopenharmony_ci	unsigned long chn_rev;
21508c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
21518c2ecf20Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	mutex_lock(&dev->cmdlock);
21548c2ecf20Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
21558c2ecf20Sopenharmony_ci	/* send the stop command */
21568c2ecf20Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
21578c2ecf20Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
21588c2ecf20Sopenharmony_ci	buffer[2] = CMD_STOP;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
21618c2ecf20Sopenharmony_ci	if (res != 0)
21628c2ecf20Sopenharmony_ci		dev_err(&dev->udev->dev, "CMD_STOP error\n");
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res);
21658c2ecf20Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
21668c2ecf20Sopenharmony_ci	return res;
21678c2ecf20Sopenharmony_ci}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_cistatic void s2255_stop_readpipe(struct s2255_dev *dev)
21708c2ecf20Sopenharmony_ci{
21718c2ecf20Sopenharmony_ci	struct s2255_pipeinfo *pipe = &dev->pipe;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	pipe->state = 0;
21748c2ecf20Sopenharmony_ci	if (pipe->stream_urb) {
21758c2ecf20Sopenharmony_ci		/* cancel urb */
21768c2ecf20Sopenharmony_ci		usb_kill_urb(pipe->stream_urb);
21778c2ecf20Sopenharmony_ci		usb_free_urb(pipe->stream_urb);
21788c2ecf20Sopenharmony_ci		pipe->stream_urb = NULL;
21798c2ecf20Sopenharmony_ci	}
21808c2ecf20Sopenharmony_ci	dprintk(dev, 4, "%s", __func__);
21818c2ecf20Sopenharmony_ci	return;
21828c2ecf20Sopenharmony_ci}
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_cistatic void s2255_fwload_start(struct s2255_dev *dev)
21858c2ecf20Sopenharmony_ci{
21868c2ecf20Sopenharmony_ci	s2255_reset_dsppower(dev);
21878c2ecf20Sopenharmony_ci	dev->fw_data->fw_size = dev->fw_data->fw->size;
21888c2ecf20Sopenharmony_ci	atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
21898c2ecf20Sopenharmony_ci	memcpy(dev->fw_data->pfw_data,
21908c2ecf20Sopenharmony_ci	       dev->fw_data->fw->data, CHUNK_SIZE);
21918c2ecf20Sopenharmony_ci	dev->fw_data->fw_loaded = CHUNK_SIZE;
21928c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
21938c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(dev->udev, 2),
21948c2ecf20Sopenharmony_ci			  dev->fw_data->pfw_data,
21958c2ecf20Sopenharmony_ci			  CHUNK_SIZE, s2255_fwchunk_complete,
21968c2ecf20Sopenharmony_ci			  dev->fw_data);
21978c2ecf20Sopenharmony_ci	mod_timer(&dev->timer, jiffies + HZ);
21988c2ecf20Sopenharmony_ci}
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci/* standard usb probe function */
22018c2ecf20Sopenharmony_cistatic int s2255_probe(struct usb_interface *interface,
22028c2ecf20Sopenharmony_ci		       const struct usb_device_id *id)
22038c2ecf20Sopenharmony_ci{
22048c2ecf20Sopenharmony_ci	struct s2255_dev *dev = NULL;
22058c2ecf20Sopenharmony_ci	struct usb_host_interface *iface_desc;
22068c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
22078c2ecf20Sopenharmony_ci	int i;
22088c2ecf20Sopenharmony_ci	int retval = -ENOMEM;
22098c2ecf20Sopenharmony_ci	__le32 *pdata;
22108c2ecf20Sopenharmony_ci	int fw_size;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	/* allocate memory for our device state and initialize it to zero */
22138c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
22148c2ecf20Sopenharmony_ci	if (dev == NULL) {
22158c2ecf20Sopenharmony_ci		s2255_dev_err(&interface->dev, "out of memory\n");
22168c2ecf20Sopenharmony_ci		return -ENOMEM;
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
22208c2ecf20Sopenharmony_ci	if (dev->cmdbuf == NULL) {
22218c2ecf20Sopenharmony_ci		s2255_dev_err(&interface->dev, "out of memory\n");
22228c2ecf20Sopenharmony_ci		goto errorFWDATA1;
22238c2ecf20Sopenharmony_ci	}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	atomic_set(&dev->num_channels, 0);
22268c2ecf20Sopenharmony_ci	dev->pid = id->idProduct;
22278c2ecf20Sopenharmony_ci	dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
22288c2ecf20Sopenharmony_ci	if (!dev->fw_data)
22298c2ecf20Sopenharmony_ci		goto errorFWDATA1;
22308c2ecf20Sopenharmony_ci	mutex_init(&dev->lock);
22318c2ecf20Sopenharmony_ci	mutex_init(&dev->cmdlock);
22328c2ecf20Sopenharmony_ci	/* grab usb_device and save it */
22338c2ecf20Sopenharmony_ci	dev->udev = usb_get_dev(interface_to_usbdev(interface));
22348c2ecf20Sopenharmony_ci	if (dev->udev == NULL) {
22358c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "null usb device\n");
22368c2ecf20Sopenharmony_ci		retval = -ENODEV;
22378c2ecf20Sopenharmony_ci		goto errorUDEV;
22388c2ecf20Sopenharmony_ci	}
22398c2ecf20Sopenharmony_ci	dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
22408c2ecf20Sopenharmony_ci		dev, dev->udev, interface);
22418c2ecf20Sopenharmony_ci	dev->interface = interface;
22428c2ecf20Sopenharmony_ci	/* set up the endpoint information  */
22438c2ecf20Sopenharmony_ci	iface_desc = interface->cur_altsetting;
22448c2ecf20Sopenharmony_ci	dev_dbg(&interface->dev, "num EP: %d\n",
22458c2ecf20Sopenharmony_ci		iface_desc->desc.bNumEndpoints);
22468c2ecf20Sopenharmony_ci	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
22478c2ecf20Sopenharmony_ci		endpoint = &iface_desc->endpoint[i].desc;
22488c2ecf20Sopenharmony_ci		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
22498c2ecf20Sopenharmony_ci			/* we found the bulk in endpoint */
22508c2ecf20Sopenharmony_ci			dev->read_endpoint = endpoint->bEndpointAddress;
22518c2ecf20Sopenharmony_ci		}
22528c2ecf20Sopenharmony_ci	}
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	if (!dev->read_endpoint) {
22558c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
22568c2ecf20Sopenharmony_ci		goto errorEP;
22578c2ecf20Sopenharmony_ci	}
22588c2ecf20Sopenharmony_ci	timer_setup(&dev->timer, s2255_timer, 0);
22598c2ecf20Sopenharmony_ci	init_waitqueue_head(&dev->fw_data->wait_fw);
22608c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
22618c2ecf20Sopenharmony_ci		struct s2255_vc *vc = &dev->vc[i];
22628c2ecf20Sopenharmony_ci		vc->idx = i;
22638c2ecf20Sopenharmony_ci		vc->dev = dev;
22648c2ecf20Sopenharmony_ci		init_waitqueue_head(&vc->wait_setmode);
22658c2ecf20Sopenharmony_ci		init_waitqueue_head(&vc->wait_vidstatus);
22668c2ecf20Sopenharmony_ci		spin_lock_init(&vc->qlock);
22678c2ecf20Sopenharmony_ci		mutex_init(&vc->vb_lock);
22688c2ecf20Sopenharmony_ci	}
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
22718c2ecf20Sopenharmony_ci	if (!dev->fw_data->fw_urb)
22728c2ecf20Sopenharmony_ci		goto errorFWURB;
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
22758c2ecf20Sopenharmony_ci	if (!dev->fw_data->pfw_data) {
22768c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "out of memory!\n");
22778c2ecf20Sopenharmony_ci		goto errorFWDATA2;
22788c2ecf20Sopenharmony_ci	}
22798c2ecf20Sopenharmony_ci	/* load the first chunk */
22808c2ecf20Sopenharmony_ci	if (request_firmware(&dev->fw_data->fw,
22818c2ecf20Sopenharmony_ci			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
22828c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n");
22838c2ecf20Sopenharmony_ci		goto errorREQFW;
22848c2ecf20Sopenharmony_ci	}
22858c2ecf20Sopenharmony_ci	/* check the firmware is valid */
22868c2ecf20Sopenharmony_ci	fw_size = dev->fw_data->fw->size;
22878c2ecf20Sopenharmony_ci	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci	if (*pdata != S2255_FW_MARKER) {
22908c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Firmware invalid.\n");
22918c2ecf20Sopenharmony_ci		retval = -ENODEV;
22928c2ecf20Sopenharmony_ci		goto errorFWMARKER;
22938c2ecf20Sopenharmony_ci	} else {
22948c2ecf20Sopenharmony_ci		/* make sure firmware is the latest */
22958c2ecf20Sopenharmony_ci		__le32 *pRel;
22968c2ecf20Sopenharmony_ci		pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
22978c2ecf20Sopenharmony_ci		pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
22988c2ecf20Sopenharmony_ci		dev->dsp_fw_ver = le32_to_cpu(*pRel);
22998c2ecf20Sopenharmony_ci		if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
23008c2ecf20Sopenharmony_ci			pr_info("s2255: f2255usb.bin out of date.\n");
23018c2ecf20Sopenharmony_ci		if (dev->pid == 0x2257 &&
23028c2ecf20Sopenharmony_ci				dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
23038c2ecf20Sopenharmony_ci			pr_warn("2257 needs firmware %d or above.\n",
23048c2ecf20Sopenharmony_ci				S2255_MIN_DSP_COLORFILTER);
23058c2ecf20Sopenharmony_ci	}
23068c2ecf20Sopenharmony_ci	usb_reset_device(dev->udev);
23078c2ecf20Sopenharmony_ci	/* load 2255 board specific */
23088c2ecf20Sopenharmony_ci	retval = s2255_board_init(dev);
23098c2ecf20Sopenharmony_ci	if (retval)
23108c2ecf20Sopenharmony_ci		goto errorBOARDINIT;
23118c2ecf20Sopenharmony_ci	s2255_fwload_start(dev);
23128c2ecf20Sopenharmony_ci	/* loads v4l specific */
23138c2ecf20Sopenharmony_ci	retval = s2255_probe_v4l(dev);
23148c2ecf20Sopenharmony_ci	if (retval)
23158c2ecf20Sopenharmony_ci		goto errorBOARDINIT;
23168c2ecf20Sopenharmony_ci	dev_info(&interface->dev, "Sensoray 2255 detected\n");
23178c2ecf20Sopenharmony_ci	return 0;
23188c2ecf20Sopenharmony_cierrorBOARDINIT:
23198c2ecf20Sopenharmony_ci	s2255_board_shutdown(dev);
23208c2ecf20Sopenharmony_cierrorFWMARKER:
23218c2ecf20Sopenharmony_ci	release_firmware(dev->fw_data->fw);
23228c2ecf20Sopenharmony_cierrorREQFW:
23238c2ecf20Sopenharmony_ci	kfree(dev->fw_data->pfw_data);
23248c2ecf20Sopenharmony_cierrorFWDATA2:
23258c2ecf20Sopenharmony_ci	usb_free_urb(dev->fw_data->fw_urb);
23268c2ecf20Sopenharmony_cierrorFWURB:
23278c2ecf20Sopenharmony_ci	del_timer_sync(&dev->timer);
23288c2ecf20Sopenharmony_cierrorEP:
23298c2ecf20Sopenharmony_ci	usb_put_dev(dev->udev);
23308c2ecf20Sopenharmony_cierrorUDEV:
23318c2ecf20Sopenharmony_ci	kfree(dev->fw_data);
23328c2ecf20Sopenharmony_ci	mutex_destroy(&dev->lock);
23338c2ecf20Sopenharmony_cierrorFWDATA1:
23348c2ecf20Sopenharmony_ci	kfree(dev->cmdbuf);
23358c2ecf20Sopenharmony_ci	kfree(dev);
23368c2ecf20Sopenharmony_ci	pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval);
23378c2ecf20Sopenharmony_ci	return retval;
23388c2ecf20Sopenharmony_ci}
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci/* disconnect routine. when board is removed physically or with rmmod */
23418c2ecf20Sopenharmony_cistatic void s2255_disconnect(struct usb_interface *interface)
23428c2ecf20Sopenharmony_ci{
23438c2ecf20Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
23448c2ecf20Sopenharmony_ci	int i;
23458c2ecf20Sopenharmony_ci	int channels = atomic_read(&dev->num_channels);
23468c2ecf20Sopenharmony_ci	mutex_lock(&dev->lock);
23478c2ecf20Sopenharmony_ci	v4l2_device_disconnect(&dev->v4l2_dev);
23488c2ecf20Sopenharmony_ci	mutex_unlock(&dev->lock);
23498c2ecf20Sopenharmony_ci	/*see comments in the uvc_driver.c usb disconnect function */
23508c2ecf20Sopenharmony_ci	atomic_inc(&dev->num_channels);
23518c2ecf20Sopenharmony_ci	/* unregister each video device. */
23528c2ecf20Sopenharmony_ci	for (i = 0; i < channels; i++)
23538c2ecf20Sopenharmony_ci		video_unregister_device(&dev->vc[i].vdev);
23548c2ecf20Sopenharmony_ci	/* wake up any of our timers */
23558c2ecf20Sopenharmony_ci	atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
23568c2ecf20Sopenharmony_ci	wake_up(&dev->fw_data->wait_fw);
23578c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
23588c2ecf20Sopenharmony_ci		dev->vc[i].setmode_ready = 1;
23598c2ecf20Sopenharmony_ci		wake_up(&dev->vc[i].wait_setmode);
23608c2ecf20Sopenharmony_ci		dev->vc[i].vidstatus_ready = 1;
23618c2ecf20Sopenharmony_ci		wake_up(&dev->vc[i].wait_vidstatus);
23628c2ecf20Sopenharmony_ci	}
23638c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&dev->num_channels))
23648c2ecf20Sopenharmony_ci		s2255_destroy(dev);
23658c2ecf20Sopenharmony_ci	dev_info(&interface->dev, "%s\n", __func__);
23668c2ecf20Sopenharmony_ci}
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_cistatic struct usb_driver s2255_driver = {
23698c2ecf20Sopenharmony_ci	.name = S2255_DRIVER_NAME,
23708c2ecf20Sopenharmony_ci	.probe = s2255_probe,
23718c2ecf20Sopenharmony_ci	.disconnect = s2255_disconnect,
23728c2ecf20Sopenharmony_ci	.id_table = s2255_table,
23738c2ecf20Sopenharmony_ci};
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_cimodule_usb_driver(s2255_driver);
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
23788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
23798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
23808c2ecf20Sopenharmony_ciMODULE_VERSION(S2255_VERSION);
23818c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_FILE_NAME);
2382