162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  s2255drv.c - a driver for the Sensoray 2255 USB video capture device
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *   Copyright (C) 2007-2014 by Sensoray Company Inc.
662306a36Sopenharmony_ci *                              Dean Anderson
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Some video buffer code based on vivi driver:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Sensoray 2255 device supports 4 simultaneous channels.
1162306a36Sopenharmony_ci * The channels are not "crossbar" inputs, they are physically
1262306a36Sopenharmony_ci * attached to separate video decoders.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Because of USB2.0 bandwidth limitations. There is only a
1562306a36Sopenharmony_ci * certain amount of data which may be transferred at one time.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Example maximum bandwidth utilization:
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * -full size, color mode YUYV or YUV422P: 2 channels at once
2062306a36Sopenharmony_ci * -full or half size Grey scale: all 4 channels at once
2162306a36Sopenharmony_ci * -half size, color mode YUYV or YUV422P: all 4 channels at once
2262306a36Sopenharmony_ci * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
2362306a36Sopenharmony_ci *  at once.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/firmware.h>
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/mutex.h>
3062306a36Sopenharmony_ci#include <linux/slab.h>
3162306a36Sopenharmony_ci#include <linux/videodev2.h>
3262306a36Sopenharmony_ci#include <linux/mm.h>
3362306a36Sopenharmony_ci#include <linux/vmalloc.h>
3462306a36Sopenharmony_ci#include <linux/usb.h>
3562306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h>
3662306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h>
3762306a36Sopenharmony_ci#include <media/v4l2-common.h>
3862306a36Sopenharmony_ci#include <media/v4l2-device.h>
3962306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
4062306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
4162306a36Sopenharmony_ci#include <media/v4l2-event.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define S2255_VERSION		"1.25.1"
4462306a36Sopenharmony_ci#define FIRMWARE_FILE_NAME "f2255usb.bin"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* default JPEG quality */
4762306a36Sopenharmony_ci#define S2255_DEF_JPEG_QUAL     50
4862306a36Sopenharmony_ci/* vendor request in */
4962306a36Sopenharmony_ci#define S2255_VR_IN		0
5062306a36Sopenharmony_ci/* vendor request out */
5162306a36Sopenharmony_ci#define S2255_VR_OUT		1
5262306a36Sopenharmony_ci/* firmware query */
5362306a36Sopenharmony_ci#define S2255_VR_FW		0x30
5462306a36Sopenharmony_ci/* USB endpoint number for configuring the device */
5562306a36Sopenharmony_ci#define S2255_CONFIG_EP         2
5662306a36Sopenharmony_ci/* maximum time for DSP to start responding after last FW word loaded(ms) */
5762306a36Sopenharmony_ci#define S2255_DSP_BOOTTIME      800
5862306a36Sopenharmony_ci/* maximum time to wait for firmware to load (ms) */
5962306a36Sopenharmony_ci#define S2255_LOAD_TIMEOUT      (5000 + S2255_DSP_BOOTTIME)
6062306a36Sopenharmony_ci#define S2255_MIN_BUFS          2
6162306a36Sopenharmony_ci#define S2255_SETMODE_TIMEOUT   500
6262306a36Sopenharmony_ci#define S2255_VIDSTATUS_TIMEOUT 350
6362306a36Sopenharmony_ci#define S2255_MARKER_FRAME	cpu_to_le32(0x2255DA4AL)
6462306a36Sopenharmony_ci#define S2255_MARKER_RESPONSE	cpu_to_le32(0x2255ACACL)
6562306a36Sopenharmony_ci#define S2255_RESPONSE_SETMODE  cpu_to_le32(0x01)
6662306a36Sopenharmony_ci#define S2255_RESPONSE_FW       cpu_to_le32(0x10)
6762306a36Sopenharmony_ci#define S2255_RESPONSE_STATUS   cpu_to_le32(0x20)
6862306a36Sopenharmony_ci#define S2255_USB_XFER_SIZE	(16 * 1024)
6962306a36Sopenharmony_ci#define MAX_CHANNELS		4
7062306a36Sopenharmony_ci#define SYS_FRAMES		4
7162306a36Sopenharmony_ci/* maximum size is PAL full size plus room for the marker header(s) */
7262306a36Sopenharmony_ci#define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
7362306a36Sopenharmony_ci#define DEF_USB_BLOCK		S2255_USB_XFER_SIZE
7462306a36Sopenharmony_ci#define LINE_SZ_4CIFS_NTSC	640
7562306a36Sopenharmony_ci#define LINE_SZ_2CIFS_NTSC	640
7662306a36Sopenharmony_ci#define LINE_SZ_1CIFS_NTSC	320
7762306a36Sopenharmony_ci#define LINE_SZ_4CIFS_PAL	704
7862306a36Sopenharmony_ci#define LINE_SZ_2CIFS_PAL	704
7962306a36Sopenharmony_ci#define LINE_SZ_1CIFS_PAL	352
8062306a36Sopenharmony_ci#define NUM_LINES_4CIFS_NTSC	240
8162306a36Sopenharmony_ci#define NUM_LINES_2CIFS_NTSC	240
8262306a36Sopenharmony_ci#define NUM_LINES_1CIFS_NTSC	240
8362306a36Sopenharmony_ci#define NUM_LINES_4CIFS_PAL	288
8462306a36Sopenharmony_ci#define NUM_LINES_2CIFS_PAL	288
8562306a36Sopenharmony_ci#define NUM_LINES_1CIFS_PAL	288
8662306a36Sopenharmony_ci#define LINE_SZ_DEF		640
8762306a36Sopenharmony_ci#define NUM_LINES_DEF		240
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* predefined settings */
9162306a36Sopenharmony_ci#define FORMAT_NTSC	1
9262306a36Sopenharmony_ci#define FORMAT_PAL	2
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
9562306a36Sopenharmony_ci#define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
9662306a36Sopenharmony_ci#define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
9762306a36Sopenharmony_ci/* SCALE_4CIFSI is the 2 fields interpolated into one */
9862306a36Sopenharmony_ci#define SCALE_4CIFSI	4	/* 640x480(NTSC) or 704x576(PAL) high quality */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define COLOR_YUVPL	1	/* YUV planar */
10162306a36Sopenharmony_ci#define COLOR_YUVPK	2	/* YUV packed */
10262306a36Sopenharmony_ci#define COLOR_Y8	4	/* monochrome */
10362306a36Sopenharmony_ci#define COLOR_JPG       5       /* JPEG */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define MASK_COLOR       0x000000ff
10662306a36Sopenharmony_ci#define MASK_JPG_QUALITY 0x0000ff00
10762306a36Sopenharmony_ci#define MASK_INPUT_TYPE  0x000f0000
10862306a36Sopenharmony_ci/* frame decimation. */
10962306a36Sopenharmony_ci#define FDEC_1		1	/* capture every frame. default */
11062306a36Sopenharmony_ci#define FDEC_2		2	/* capture every 2nd frame */
11162306a36Sopenharmony_ci#define FDEC_3		3	/* capture every 3rd frame */
11262306a36Sopenharmony_ci#define FDEC_5		5	/* capture every 5th frame */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/*-------------------------------------------------------
11562306a36Sopenharmony_ci * Default mode parameters.
11662306a36Sopenharmony_ci *-------------------------------------------------------*/
11762306a36Sopenharmony_ci#define DEF_SCALE	SCALE_4CIFS
11862306a36Sopenharmony_ci#define DEF_COLOR	COLOR_YUVPL
11962306a36Sopenharmony_ci#define DEF_FDEC	FDEC_1
12062306a36Sopenharmony_ci#define DEF_BRIGHT	0
12162306a36Sopenharmony_ci#define DEF_CONTRAST	0x5c
12262306a36Sopenharmony_ci#define DEF_SATURATION	0x80
12362306a36Sopenharmony_ci#define DEF_HUE		0
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* usb config commands */
12662306a36Sopenharmony_ci#define IN_DATA_TOKEN	cpu_to_le32(0x2255c0de)
12762306a36Sopenharmony_ci#define CMD_2255	0xc2255000
12862306a36Sopenharmony_ci#define CMD_SET_MODE	cpu_to_le32((CMD_2255 | 0x10))
12962306a36Sopenharmony_ci#define CMD_START	cpu_to_le32((CMD_2255 | 0x20))
13062306a36Sopenharmony_ci#define CMD_STOP	cpu_to_le32((CMD_2255 | 0x30))
13162306a36Sopenharmony_ci#define CMD_STATUS	cpu_to_le32((CMD_2255 | 0x40))
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistruct s2255_mode {
13462306a36Sopenharmony_ci	u32 format;	/* input video format (NTSC, PAL) */
13562306a36Sopenharmony_ci	u32 scale;	/* output video scale */
13662306a36Sopenharmony_ci	u32 color;	/* output video color format */
13762306a36Sopenharmony_ci	u32 fdec;	/* frame decimation */
13862306a36Sopenharmony_ci	u32 bright;	/* brightness */
13962306a36Sopenharmony_ci	u32 contrast;	/* contrast */
14062306a36Sopenharmony_ci	u32 saturation;	/* saturation */
14162306a36Sopenharmony_ci	u32 hue;	/* hue (NTSC only)*/
14262306a36Sopenharmony_ci	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
14362306a36Sopenharmony_ci	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
14462306a36Sopenharmony_ci	u32 restart;	/* if DSP requires restart */
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define S2255_READ_IDLE		0
14962306a36Sopenharmony_ci#define S2255_READ_FRAME	1
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* frame structure */
15262306a36Sopenharmony_cistruct s2255_framei {
15362306a36Sopenharmony_ci	unsigned long size;
15462306a36Sopenharmony_ci	unsigned long ulState;	/* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
15562306a36Sopenharmony_ci	void *lpvbits;		/* image data */
15662306a36Sopenharmony_ci	unsigned long cur_size;	/* current data copied to it */
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* image buffer structure */
16062306a36Sopenharmony_cistruct s2255_bufferi {
16162306a36Sopenharmony_ci	unsigned long dwFrames;			/* number of frames in buffer */
16262306a36Sopenharmony_ci	struct s2255_framei frame[SYS_FRAMES];	/* array of FRAME structures */
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define DEF_MODEI_NTSC_CONT	{FORMAT_NTSC, DEF_SCALE, DEF_COLOR,	\
16662306a36Sopenharmony_ci			DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
16762306a36Sopenharmony_ci			DEF_HUE, 0, DEF_USB_BLOCK, 0}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/* for firmware loading, fw_state */
17062306a36Sopenharmony_ci#define S2255_FW_NOTLOADED	0
17162306a36Sopenharmony_ci#define S2255_FW_LOADED_DSPWAIT	1
17262306a36Sopenharmony_ci#define S2255_FW_SUCCESS	2
17362306a36Sopenharmony_ci#define S2255_FW_FAILED		3
17462306a36Sopenharmony_ci#define S2255_FW_DISCONNECTING  4
17562306a36Sopenharmony_ci#define S2255_FW_MARKER		cpu_to_le32(0x22552f2f)
17662306a36Sopenharmony_ci/* 2255 read states */
17762306a36Sopenharmony_ci#define S2255_READ_IDLE         0
17862306a36Sopenharmony_ci#define S2255_READ_FRAME        1
17962306a36Sopenharmony_cistruct s2255_fw {
18062306a36Sopenharmony_ci	int		      fw_loaded;
18162306a36Sopenharmony_ci	int		      fw_size;
18262306a36Sopenharmony_ci	struct urb	      *fw_urb;
18362306a36Sopenharmony_ci	atomic_t	      fw_state;
18462306a36Sopenharmony_ci	void		      *pfw_data;
18562306a36Sopenharmony_ci	wait_queue_head_t     wait_fw;
18662306a36Sopenharmony_ci	const struct firmware *fw;
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistruct s2255_pipeinfo {
19062306a36Sopenharmony_ci	u32 max_transfer_size;
19162306a36Sopenharmony_ci	u32 cur_transfer_size;
19262306a36Sopenharmony_ci	u8 *transfer_buffer;
19362306a36Sopenharmony_ci	u32 state;
19462306a36Sopenharmony_ci	void *stream_urb;
19562306a36Sopenharmony_ci	void *dev;	/* back pointer to s2255_dev struct*/
19662306a36Sopenharmony_ci	u32 err_count;
19762306a36Sopenharmony_ci	u32 idx;
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistruct s2255_fmt; /*forward declaration */
20162306a36Sopenharmony_cistruct s2255_dev;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* 2255 video channel */
20462306a36Sopenharmony_cistruct s2255_vc {
20562306a36Sopenharmony_ci	struct s2255_dev        *dev;
20662306a36Sopenharmony_ci	struct video_device	vdev;
20762306a36Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
20862306a36Sopenharmony_ci	struct v4l2_ctrl	*jpegqual_ctrl;
20962306a36Sopenharmony_ci	int			resources;
21062306a36Sopenharmony_ci	struct list_head        buf_list;
21162306a36Sopenharmony_ci	struct s2255_bufferi	buffer;
21262306a36Sopenharmony_ci	struct s2255_mode	mode;
21362306a36Sopenharmony_ci	v4l2_std_id		std;
21462306a36Sopenharmony_ci	/* jpeg compression */
21562306a36Sopenharmony_ci	unsigned		jpegqual;
21662306a36Sopenharmony_ci	/* capture parameters (for high quality mode full size) */
21762306a36Sopenharmony_ci	struct v4l2_captureparm cap_parm;
21862306a36Sopenharmony_ci	int			cur_frame;
21962306a36Sopenharmony_ci	int			last_frame;
22062306a36Sopenharmony_ci	/* allocated image size */
22162306a36Sopenharmony_ci	unsigned long		req_image_size;
22262306a36Sopenharmony_ci	/* received packet size */
22362306a36Sopenharmony_ci	unsigned long		pkt_size;
22462306a36Sopenharmony_ci	int			bad_payload;
22562306a36Sopenharmony_ci	unsigned long		frame_count;
22662306a36Sopenharmony_ci	/* if JPEG image */
22762306a36Sopenharmony_ci	int                     jpg_size;
22862306a36Sopenharmony_ci	/* if channel configured to default state */
22962306a36Sopenharmony_ci	int                     configured;
23062306a36Sopenharmony_ci	wait_queue_head_t       wait_setmode;
23162306a36Sopenharmony_ci	int                     setmode_ready;
23262306a36Sopenharmony_ci	/* video status items */
23362306a36Sopenharmony_ci	int                     vidstatus;
23462306a36Sopenharmony_ci	wait_queue_head_t       wait_vidstatus;
23562306a36Sopenharmony_ci	int                     vidstatus_ready;
23662306a36Sopenharmony_ci	unsigned int		width;
23762306a36Sopenharmony_ci	unsigned int		height;
23862306a36Sopenharmony_ci	enum v4l2_field         field;
23962306a36Sopenharmony_ci	const struct s2255_fmt	*fmt;
24062306a36Sopenharmony_ci	int idx; /* channel number on device, 0-3 */
24162306a36Sopenharmony_ci	struct vb2_queue vb_vidq;
24262306a36Sopenharmony_ci	struct mutex vb_lock; /* streaming lock */
24362306a36Sopenharmony_ci	spinlock_t qlock;
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistruct s2255_dev {
24862306a36Sopenharmony_ci	struct s2255_vc         vc[MAX_CHANNELS];
24962306a36Sopenharmony_ci	struct v4l2_device      v4l2_dev;
25062306a36Sopenharmony_ci	atomic_t                num_channels;
25162306a36Sopenharmony_ci	int			frames;
25262306a36Sopenharmony_ci	struct mutex		lock;	/* channels[].vdev.lock */
25362306a36Sopenharmony_ci	struct mutex		cmdlock; /* protects cmdbuf */
25462306a36Sopenharmony_ci	struct usb_device	*udev;
25562306a36Sopenharmony_ci	struct usb_interface	*interface;
25662306a36Sopenharmony_ci	u8			read_endpoint;
25762306a36Sopenharmony_ci	struct timer_list	timer;
25862306a36Sopenharmony_ci	struct s2255_fw	*fw_data;
25962306a36Sopenharmony_ci	struct s2255_pipeinfo	pipe;
26062306a36Sopenharmony_ci	u32			cc;	/* current channel */
26162306a36Sopenharmony_ci	int			frame_ready;
26262306a36Sopenharmony_ci	int                     chn_ready;
26362306a36Sopenharmony_ci	/* dsp firmware version (f2255usb.bin) */
26462306a36Sopenharmony_ci	int                     dsp_fw_ver;
26562306a36Sopenharmony_ci	u16                     pid; /* product id */
26662306a36Sopenharmony_ci#define S2255_CMDBUF_SIZE 512
26762306a36Sopenharmony_ci	__le32                  *cmdbuf;
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistruct s2255_fmt {
27662306a36Sopenharmony_ci	u32 fourcc;
27762306a36Sopenharmony_ci	int depth;
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* buffer for one video frame */
28162306a36Sopenharmony_cistruct s2255_buffer {
28262306a36Sopenharmony_ci	/* common v4l buffer stuff -- must be first */
28362306a36Sopenharmony_ci	struct vb2_v4l2_buffer vb;
28462306a36Sopenharmony_ci	struct list_head list;
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/* current cypress EEPROM firmware version */
28962306a36Sopenharmony_ci#define S2255_CUR_USB_FWVER	((3 << 8) | 12)
29062306a36Sopenharmony_ci/* current DSP FW version */
29162306a36Sopenharmony_ci#define S2255_CUR_DSP_FWVER     10104
29262306a36Sopenharmony_ci/* Need DSP version 5+ for video status feature */
29362306a36Sopenharmony_ci#define S2255_MIN_DSP_STATUS      5
29462306a36Sopenharmony_ci#define S2255_MIN_DSP_COLORFILTER 8
29562306a36Sopenharmony_ci#define S2255_NORMS		(V4L2_STD_ALL)
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* private V4L2 controls */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/*
30062306a36Sopenharmony_ci * The following chart displays how COLORFILTER should be set
30162306a36Sopenharmony_ci *  =========================================================
30262306a36Sopenharmony_ci *  =     fourcc              =     COLORFILTER             =
30362306a36Sopenharmony_ci *  =                         ===============================
30462306a36Sopenharmony_ci *  =                         =   0             =    1      =
30562306a36Sopenharmony_ci *  =========================================================
30662306a36Sopenharmony_ci *  =  V4L2_PIX_FMT_GREY(Y8)  = monochrome from = monochrome=
30762306a36Sopenharmony_ci *  =                         = s-video or      = composite =
30862306a36Sopenharmony_ci *  =                         = B/W camera      = input     =
30962306a36Sopenharmony_ci *  =========================================================
31062306a36Sopenharmony_ci *  =    other                = color, svideo   = color,    =
31162306a36Sopenharmony_ci *  =                         =                 = composite =
31262306a36Sopenharmony_ci *  =========================================================
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * Notes:
31562306a36Sopenharmony_ci *   channels 0-3 on 2255 are composite
31662306a36Sopenharmony_ci *   channels 0-1 on 2257 are composite, 2-3 are s-video
31762306a36Sopenharmony_ci * If COLORFILTER is 0 with a composite color camera connected,
31862306a36Sopenharmony_ci * the output will appear monochrome but hatching
31962306a36Sopenharmony_ci * will occur.
32062306a36Sopenharmony_ci * COLORFILTER is different from "color killer" and "color effects"
32162306a36Sopenharmony_ci * for reasons above.
32262306a36Sopenharmony_ci */
32362306a36Sopenharmony_ci#define S2255_V4L2_YC_ON  1
32462306a36Sopenharmony_ci#define S2255_V4L2_YC_OFF 0
32562306a36Sopenharmony_ci#define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0)
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/* frame prefix size (sent once every frame) */
32862306a36Sopenharmony_ci#define PREFIX_SIZE		512
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/* Channels on box are in reverse order */
33162306a36Sopenharmony_cistatic unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int debug;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int s2255_start_readpipe(struct s2255_dev *dev);
33662306a36Sopenharmony_cistatic void s2255_stop_readpipe(struct s2255_dev *dev);
33762306a36Sopenharmony_cistatic int s2255_start_acquire(struct s2255_vc *vc);
33862306a36Sopenharmony_cistatic int s2255_stop_acquire(struct s2255_vc *vc);
33962306a36Sopenharmony_cistatic void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf,
34062306a36Sopenharmony_ci			   int jpgsize);
34162306a36Sopenharmony_cistatic int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode);
34262306a36Sopenharmony_cistatic int s2255_board_shutdown(struct s2255_dev *dev);
34362306a36Sopenharmony_cistatic void s2255_fwload_start(struct s2255_dev *dev);
34462306a36Sopenharmony_cistatic void s2255_destroy(struct s2255_dev *dev);
34562306a36Sopenharmony_cistatic long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
34662306a36Sopenharmony_ci			     u16 index, u16 value, void *buf,
34762306a36Sopenharmony_ci			     s32 buf_len, int bOut);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/* dev_err macro with driver name */
35062306a36Sopenharmony_ci#define S2255_DRIVER_NAME "s2255"
35162306a36Sopenharmony_ci#define s2255_dev_err(dev, fmt, arg...)					\
35262306a36Sopenharmony_ci		dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci#define dprintk(dev, level, fmt, arg...) \
35562306a36Sopenharmony_ci	v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic struct usb_driver s2255_driver;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/* start video number */
36062306a36Sopenharmony_cistatic int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/* Enable jpeg capture. */
36362306a36Sopenharmony_cistatic int jpeg_enable = 1;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cimodule_param(debug, int, 0644);
36662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
36762306a36Sopenharmony_cimodule_param(video_nr, int, 0644);
36862306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
36962306a36Sopenharmony_cimodule_param(jpeg_enable, int, 0644);
37062306a36Sopenharmony_ciMODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/* USB device table */
37362306a36Sopenharmony_ci#define USB_SENSORAY_VID	0x1943
37462306a36Sopenharmony_cistatic const struct usb_device_id s2255_table[] = {
37562306a36Sopenharmony_ci	{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
37662306a36Sopenharmony_ci	{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
37762306a36Sopenharmony_ci	{ }			/* Terminating entry */
37862306a36Sopenharmony_ci};
37962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, s2255_table);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci#define BUFFER_TIMEOUT msecs_to_jiffies(400)
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/* image formats.  */
38462306a36Sopenharmony_ci/* JPEG formats must be defined last to support jpeg_enable parameter */
38562306a36Sopenharmony_cistatic const struct s2255_fmt formats[] = {
38662306a36Sopenharmony_ci	{
38762306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_YUYV,
38862306a36Sopenharmony_ci		.depth = 16
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	}, {
39162306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_UYVY,
39262306a36Sopenharmony_ci		.depth = 16
39362306a36Sopenharmony_ci	}, {
39462306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_YUV422P,
39562306a36Sopenharmony_ci		.depth = 16
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	}, {
39862306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_GREY,
39962306a36Sopenharmony_ci		.depth = 8
40062306a36Sopenharmony_ci	}, {
40162306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_JPEG,
40262306a36Sopenharmony_ci		.depth = 24
40362306a36Sopenharmony_ci	}, {
40462306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_MJPEG,
40562306a36Sopenharmony_ci		.depth = 24
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci};
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int norm_maxw(struct s2255_vc *vc)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
41262306a36Sopenharmony_ci	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int norm_maxh(struct s2255_vc *vc)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
41862306a36Sopenharmony_ci	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int norm_minw(struct s2255_vc *vc)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
42462306a36Sopenharmony_ci	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic int norm_minh(struct s2255_vc *vc)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	return (vc->std & V4L2_STD_525_60) ?
43062306a36Sopenharmony_ci	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/*
43562306a36Sopenharmony_ci * TODO: fixme: move YUV reordering to hardware
43662306a36Sopenharmony_ci * converts 2255 planar format to yuyv or uyvy
43762306a36Sopenharmony_ci */
43862306a36Sopenharmony_cistatic void planar422p_to_yuv_packed(const unsigned char *in,
43962306a36Sopenharmony_ci				     unsigned char *out,
44062306a36Sopenharmony_ci				     int width, int height,
44162306a36Sopenharmony_ci				     int fmt)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	unsigned char *pY;
44462306a36Sopenharmony_ci	unsigned char *pCb;
44562306a36Sopenharmony_ci	unsigned char *pCr;
44662306a36Sopenharmony_ci	unsigned long size = height * width;
44762306a36Sopenharmony_ci	unsigned int i;
44862306a36Sopenharmony_ci	pY = (unsigned char *)in;
44962306a36Sopenharmony_ci	pCr = (unsigned char *)in + height * width;
45062306a36Sopenharmony_ci	pCb = (unsigned char *)in + height * width + (height * width / 2);
45162306a36Sopenharmony_ci	for (i = 0; i < size * 2; i += 4) {
45262306a36Sopenharmony_ci		out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
45362306a36Sopenharmony_ci		out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
45462306a36Sopenharmony_ci		out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
45562306a36Sopenharmony_ci		out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci	return;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void s2255_reset_dsppower(struct s2255_dev *dev)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
46362306a36Sopenharmony_ci	msleep(50);
46462306a36Sopenharmony_ci	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
46562306a36Sopenharmony_ci	msleep(600);
46662306a36Sopenharmony_ci	s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
46762306a36Sopenharmony_ci	return;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci/* kickstarts the firmware loading. from probe
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_cistatic void s2255_timer(struct timer_list *t)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct s2255_dev *dev = from_timer(dev, t, timer);
47562306a36Sopenharmony_ci	struct s2255_fw *data = dev->fw_data;
47662306a36Sopenharmony_ci	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
47762306a36Sopenharmony_ci		pr_err("s2255: can't submit urb\n");
47862306a36Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_FAILED);
47962306a36Sopenharmony_ci		/* wake up anything waiting for the firmware */
48062306a36Sopenharmony_ci		wake_up(&data->wait_fw);
48162306a36Sopenharmony_ci		return;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* this loads the firmware asynchronously.
48762306a36Sopenharmony_ci   Originally this was done synchronously in probe.
48862306a36Sopenharmony_ci   But it is better to load it asynchronously here than block
48962306a36Sopenharmony_ci   inside the probe function. Blocking inside probe affects boot time.
49062306a36Sopenharmony_ci   FW loading is triggered by the timer in the probe function
49162306a36Sopenharmony_ci*/
49262306a36Sopenharmony_cistatic void s2255_fwchunk_complete(struct urb *urb)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct s2255_fw *data = urb->context;
49562306a36Sopenharmony_ci	struct usb_device *udev = urb->dev;
49662306a36Sopenharmony_ci	int len;
49762306a36Sopenharmony_ci	if (urb->status) {
49862306a36Sopenharmony_ci		dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
49962306a36Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_FAILED);
50062306a36Sopenharmony_ci		/* wake up anything waiting for the firmware */
50162306a36Sopenharmony_ci		wake_up(&data->wait_fw);
50262306a36Sopenharmony_ci		return;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci	if (data->fw_urb == NULL) {
50562306a36Sopenharmony_ci		s2255_dev_err(&udev->dev, "disconnected\n");
50662306a36Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_FAILED);
50762306a36Sopenharmony_ci		/* wake up anything waiting for the firmware */
50862306a36Sopenharmony_ci		wake_up(&data->wait_fw);
50962306a36Sopenharmony_ci		return;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci#define CHUNK_SIZE 512
51262306a36Sopenharmony_ci	/* all USB transfers must be done with continuous kernel memory.
51362306a36Sopenharmony_ci	   can't allocate more than 128k in current linux kernel, so
51462306a36Sopenharmony_ci	   upload the firmware in chunks
51562306a36Sopenharmony_ci	 */
51662306a36Sopenharmony_ci	if (data->fw_loaded < data->fw_size) {
51762306a36Sopenharmony_ci		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
51862306a36Sopenharmony_ci		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (len < CHUNK_SIZE)
52162306a36Sopenharmony_ci			memset(data->pfw_data, 0, CHUNK_SIZE);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		memcpy(data->pfw_data,
52462306a36Sopenharmony_ci		       (char *) data->fw->data + data->fw_loaded, len);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
52762306a36Sopenharmony_ci				  data->pfw_data, CHUNK_SIZE,
52862306a36Sopenharmony_ci				  s2255_fwchunk_complete, data);
52962306a36Sopenharmony_ci		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
53062306a36Sopenharmony_ci			dev_err(&udev->dev, "failed submit URB\n");
53162306a36Sopenharmony_ci			atomic_set(&data->fw_state, S2255_FW_FAILED);
53262306a36Sopenharmony_ci			/* wake up anything waiting for the firmware */
53362306a36Sopenharmony_ci			wake_up(&data->wait_fw);
53462306a36Sopenharmony_ci			return;
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci		data->fw_loaded += len;
53762306a36Sopenharmony_ci	} else
53862306a36Sopenharmony_ci		atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
53962306a36Sopenharmony_ci	return;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void s2255_got_frame(struct s2255_vc *vc, int jpgsize)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct s2255_buffer *buf;
54662306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
54762306a36Sopenharmony_ci	unsigned long flags = 0;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
55062306a36Sopenharmony_ci	if (list_empty(&vc->buf_list)) {
55162306a36Sopenharmony_ci		dprintk(dev, 1, "No active queue to serve\n");
55262306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc->qlock, flags);
55362306a36Sopenharmony_ci		return;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	buf = list_entry(vc->buf_list.next,
55662306a36Sopenharmony_ci			 struct s2255_buffer, list);
55762306a36Sopenharmony_ci	list_del(&buf->list);
55862306a36Sopenharmony_ci	buf->vb.vb2_buf.timestamp = ktime_get_ns();
55962306a36Sopenharmony_ci	buf->vb.field = vc->field;
56062306a36Sopenharmony_ci	buf->vb.sequence = vc->frame_count;
56162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	s2255_fillbuff(vc, buf, jpgsize);
56462306a36Sopenharmony_ci	/* tell v4l buffer was filled */
56562306a36Sopenharmony_ci	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
56662306a36Sopenharmony_ci	dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic const struct s2255_fmt *format_by_fourcc(int fourcc)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	unsigned int i;
57262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++) {
57362306a36Sopenharmony_ci		if (-1 == formats[i].fourcc)
57462306a36Sopenharmony_ci			continue;
57562306a36Sopenharmony_ci		if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
57662306a36Sopenharmony_ci				     (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
57762306a36Sopenharmony_ci			continue;
57862306a36Sopenharmony_ci		if (formats[i].fourcc == fourcc)
57962306a36Sopenharmony_ci			return formats + i;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	return NULL;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci/* video buffer vmalloc implementation based partly on VIVI driver which is
58562306a36Sopenharmony_ci *          Copyright (c) 2006 by
58662306a36Sopenharmony_ci *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
58762306a36Sopenharmony_ci *                  Ted Walther <ted--a.t--enumera.com>
58862306a36Sopenharmony_ci *                  John Sokol <sokol--a.t--videotechnology.com>
58962306a36Sopenharmony_ci *                  http://v4l.videotechnology.com/
59062306a36Sopenharmony_ci *
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_cistatic void s2255_fillbuff(struct s2255_vc *vc,
59362306a36Sopenharmony_ci			   struct s2255_buffer *buf, int jpgsize)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	int pos = 0;
59662306a36Sopenharmony_ci	const char *tmpbuf;
59762306a36Sopenharmony_ci	char *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
59862306a36Sopenharmony_ci	unsigned long last_frame;
59962306a36Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (!vbuf)
60262306a36Sopenharmony_ci		return;
60362306a36Sopenharmony_ci	last_frame = vc->last_frame;
60462306a36Sopenharmony_ci	if (last_frame != -1) {
60562306a36Sopenharmony_ci		tmpbuf =
60662306a36Sopenharmony_ci		    (const char *)vc->buffer.frame[last_frame].lpvbits;
60762306a36Sopenharmony_ci		switch (vc->fmt->fourcc) {
60862306a36Sopenharmony_ci		case V4L2_PIX_FMT_YUYV:
60962306a36Sopenharmony_ci		case V4L2_PIX_FMT_UYVY:
61062306a36Sopenharmony_ci			planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
61162306a36Sopenharmony_ci						 vbuf, vc->width,
61262306a36Sopenharmony_ci						 vc->height,
61362306a36Sopenharmony_ci						 vc->fmt->fourcc);
61462306a36Sopenharmony_ci			break;
61562306a36Sopenharmony_ci		case V4L2_PIX_FMT_GREY:
61662306a36Sopenharmony_ci			memcpy(vbuf, tmpbuf, vc->width * vc->height);
61762306a36Sopenharmony_ci			break;
61862306a36Sopenharmony_ci		case V4L2_PIX_FMT_JPEG:
61962306a36Sopenharmony_ci		case V4L2_PIX_FMT_MJPEG:
62062306a36Sopenharmony_ci			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
62162306a36Sopenharmony_ci			memcpy(vbuf, tmpbuf, jpgsize);
62262306a36Sopenharmony_ci			break;
62362306a36Sopenharmony_ci		case V4L2_PIX_FMT_YUV422P:
62462306a36Sopenharmony_ci			memcpy(vbuf, tmpbuf,
62562306a36Sopenharmony_ci			       vc->width * vc->height * 2);
62662306a36Sopenharmony_ci			break;
62762306a36Sopenharmony_ci		default:
62862306a36Sopenharmony_ci			pr_info("s2255: unknown format?\n");
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci		vc->last_frame = -1;
63162306a36Sopenharmony_ci	} else {
63262306a36Sopenharmony_ci		pr_err("s2255: =======no frame\n");
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci	dprintk(dev, 2, "s2255fill at : Buffer %p size= %d\n",
63662306a36Sopenharmony_ci		vbuf, pos);
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/* ------------------------------------------------------------------
64162306a36Sopenharmony_ci   Videobuf operations
64262306a36Sopenharmony_ci   ------------------------------------------------------------------*/
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
64562306a36Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
64662306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vq);
64962306a36Sopenharmony_ci	if (*nbuffers < S2255_MIN_BUFS)
65062306a36Sopenharmony_ci		*nbuffers = S2255_MIN_BUFS;
65162306a36Sopenharmony_ci	*nplanes = 1;
65262306a36Sopenharmony_ci	sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3);
65362306a36Sopenharmony_ci	return 0;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
65962306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
66062306a36Sopenharmony_ci	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
66162306a36Sopenharmony_ci	int w = vc->width;
66262306a36Sopenharmony_ci	int h = vc->height;
66362306a36Sopenharmony_ci	unsigned long size;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	dprintk(vc->dev, 4, "%s\n", __func__);
66662306a36Sopenharmony_ci	if (vc->fmt == NULL)
66762306a36Sopenharmony_ci		return -EINVAL;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if ((w < norm_minw(vc)) ||
67062306a36Sopenharmony_ci	    (w > norm_maxw(vc)) ||
67162306a36Sopenharmony_ci	    (h < norm_minh(vc)) ||
67262306a36Sopenharmony_ci	    (h > norm_maxh(vc))) {
67362306a36Sopenharmony_ci		dprintk(vc->dev, 4, "invalid buffer prepare\n");
67462306a36Sopenharmony_ci		return -EINVAL;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	size = w * h * (vc->fmt->depth >> 3);
67762306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
67862306a36Sopenharmony_ci		dprintk(vc->dev, 4, "invalid buffer prepare\n");
67962306a36Sopenharmony_ci		return -EINVAL;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
68362306a36Sopenharmony_ci	return 0;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
68962306a36Sopenharmony_ci	struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb);
69062306a36Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
69162306a36Sopenharmony_ci	unsigned long flags = 0;
69262306a36Sopenharmony_ci	dprintk(vc->dev, 1, "%s\n", __func__);
69362306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
69462306a36Sopenharmony_ci	list_add_tail(&buf->list, &vc->buf_list);
69562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count);
69962306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic const struct vb2_ops s2255_video_qops = {
70262306a36Sopenharmony_ci	.queue_setup = queue_setup,
70362306a36Sopenharmony_ci	.buf_prepare = buffer_prepare,
70462306a36Sopenharmony_ci	.buf_queue = buffer_queue,
70562306a36Sopenharmony_ci	.start_streaming = start_streaming,
70662306a36Sopenharmony_ci	.stop_streaming = stop_streaming,
70762306a36Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
70862306a36Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
70962306a36Sopenharmony_ci};
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv,
71262306a36Sopenharmony_ci			   struct v4l2_capability *cap)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
71562306a36Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	strscpy(cap->driver, "s2255", sizeof(cap->driver));
71862306a36Sopenharmony_ci	strscpy(cap->card, "s2255", sizeof(cap->card));
71962306a36Sopenharmony_ci	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
72062306a36Sopenharmony_ci	return 0;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
72462306a36Sopenharmony_ci			       struct v4l2_fmtdesc *f)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	int index = f->index;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (index >= ARRAY_SIZE(formats))
72962306a36Sopenharmony_ci		return -EINVAL;
73062306a36Sopenharmony_ci	if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
73162306a36Sopenharmony_ci			(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
73262306a36Sopenharmony_ci		return -EINVAL;
73362306a36Sopenharmony_ci	f->pixelformat = formats[index].fourcc;
73462306a36Sopenharmony_ci	return 0;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
73862306a36Sopenharmony_ci			    struct v4l2_format *f)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
74162306a36Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	f->fmt.pix.width = vc->width;
74462306a36Sopenharmony_ci	f->fmt.pix.height = vc->height;
74562306a36Sopenharmony_ci	if (f->fmt.pix.height >=
74662306a36Sopenharmony_ci	    (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2)
74762306a36Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
74862306a36Sopenharmony_ci	else
74962306a36Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_TOP;
75062306a36Sopenharmony_ci	f->fmt.pix.pixelformat = vc->fmt->fourcc;
75162306a36Sopenharmony_ci	f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3);
75262306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
75362306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
75862306a36Sopenharmony_ci			      struct v4l2_format *f)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	const struct s2255_fmt *fmt;
76162306a36Sopenharmony_ci	enum v4l2_field field;
76262306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
76362306a36Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (fmt == NULL)
76862306a36Sopenharmony_ci		return -EINVAL;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n",
77162306a36Sopenharmony_ci		__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
77262306a36Sopenharmony_ci	if (is_ntsc) {
77362306a36Sopenharmony_ci		/* NTSC */
77462306a36Sopenharmony_ci		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
77562306a36Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
77662306a36Sopenharmony_ci			field = V4L2_FIELD_INTERLACED;
77762306a36Sopenharmony_ci		} else {
77862306a36Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
77962306a36Sopenharmony_ci			field = V4L2_FIELD_TOP;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
78262306a36Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
78362306a36Sopenharmony_ci		else
78462306a36Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
78562306a36Sopenharmony_ci	} else {
78662306a36Sopenharmony_ci		/* PAL */
78762306a36Sopenharmony_ci		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
78862306a36Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
78962306a36Sopenharmony_ci			field = V4L2_FIELD_INTERLACED;
79062306a36Sopenharmony_ci		} else {
79162306a36Sopenharmony_ci			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
79262306a36Sopenharmony_ci			field = V4L2_FIELD_TOP;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL)
79562306a36Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
79662306a36Sopenharmony_ci		else
79762306a36Sopenharmony_ci			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci	f->fmt.pix.field = field;
80062306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
80162306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
80262306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
80362306a36Sopenharmony_ci	dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__,
80462306a36Sopenharmony_ci		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
80562306a36Sopenharmony_ci	return 0;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
80962306a36Sopenharmony_ci			    struct v4l2_format *f)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
81262306a36Sopenharmony_ci	const struct s2255_fmt *fmt;
81362306a36Sopenharmony_ci	struct vb2_queue *q = &vc->vb_vidq;
81462306a36Sopenharmony_ci	struct s2255_mode mode;
81562306a36Sopenharmony_ci	int ret;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ret = vidioc_try_fmt_vid_cap(file, vc, f);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (ret < 0)
82062306a36Sopenharmony_ci		return ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (fmt == NULL)
82562306a36Sopenharmony_ci		return -EINVAL;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (vb2_is_busy(q)) {
82862306a36Sopenharmony_ci		dprintk(vc->dev, 1, "queue busy\n");
82962306a36Sopenharmony_ci		return -EBUSY;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	mode = vc->mode;
83362306a36Sopenharmony_ci	vc->fmt = fmt;
83462306a36Sopenharmony_ci	vc->width = f->fmt.pix.width;
83562306a36Sopenharmony_ci	vc->height = f->fmt.pix.height;
83662306a36Sopenharmony_ci	vc->field = f->fmt.pix.field;
83762306a36Sopenharmony_ci	if (vc->width > norm_minw(vc)) {
83862306a36Sopenharmony_ci		if (vc->height > norm_minh(vc)) {
83962306a36Sopenharmony_ci			if (vc->cap_parm.capturemode &
84062306a36Sopenharmony_ci			    V4L2_MODE_HIGHQUALITY)
84162306a36Sopenharmony_ci				mode.scale = SCALE_4CIFSI;
84262306a36Sopenharmony_ci			else
84362306a36Sopenharmony_ci				mode.scale = SCALE_4CIFS;
84462306a36Sopenharmony_ci		} else
84562306a36Sopenharmony_ci			mode.scale = SCALE_2CIFS;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	} else {
84862306a36Sopenharmony_ci		mode.scale = SCALE_1CIFS;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci	/* color mode */
85162306a36Sopenharmony_ci	switch (vc->fmt->fourcc) {
85262306a36Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
85362306a36Sopenharmony_ci		mode.color &= ~MASK_COLOR;
85462306a36Sopenharmony_ci		mode.color |= COLOR_Y8;
85562306a36Sopenharmony_ci		break;
85662306a36Sopenharmony_ci	case V4L2_PIX_FMT_JPEG:
85762306a36Sopenharmony_ci	case V4L2_PIX_FMT_MJPEG:
85862306a36Sopenharmony_ci		mode.color &= ~MASK_COLOR;
85962306a36Sopenharmony_ci		mode.color |= COLOR_JPG;
86062306a36Sopenharmony_ci		mode.color |= (vc->jpegqual << 8);
86162306a36Sopenharmony_ci		break;
86262306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
86362306a36Sopenharmony_ci		mode.color &= ~MASK_COLOR;
86462306a36Sopenharmony_ci		mode.color |= COLOR_YUVPL;
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
86762306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
86862306a36Sopenharmony_ci	default:
86962306a36Sopenharmony_ci		mode.color &= ~MASK_COLOR;
87062306a36Sopenharmony_ci		mode.color |= COLOR_YUVPK;
87162306a36Sopenharmony_ci		break;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR))
87462306a36Sopenharmony_ci		mode.restart = 1;
87562306a36Sopenharmony_ci	else if (mode.scale != vc->mode.scale)
87662306a36Sopenharmony_ci		mode.restart = 1;
87762306a36Sopenharmony_ci	else if (mode.format != vc->mode.format)
87862306a36Sopenharmony_ci		mode.restart = 1;
87962306a36Sopenharmony_ci	vc->mode = mode;
88062306a36Sopenharmony_ci	(void) s2255_set_mode(vc, &mode);
88162306a36Sopenharmony_ci	return 0;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci/* write to the configuration pipe, synchronously */
88662306a36Sopenharmony_cistatic int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
88762306a36Sopenharmony_ci			      int size)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	int pipe;
89062306a36Sopenharmony_ci	int done;
89162306a36Sopenharmony_ci	long retval = -1;
89262306a36Sopenharmony_ci	if (udev) {
89362306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
89462306a36Sopenharmony_ci		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci	return retval;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic u32 get_transfer_size(struct s2255_mode *mode)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	int linesPerFrame = LINE_SZ_DEF;
90262306a36Sopenharmony_ci	int pixelsPerLine = NUM_LINES_DEF;
90362306a36Sopenharmony_ci	u32 outImageSize;
90462306a36Sopenharmony_ci	u32 usbInSize;
90562306a36Sopenharmony_ci	unsigned int mask_mult;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (mode == NULL)
90862306a36Sopenharmony_ci		return 0;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (mode->format == FORMAT_NTSC) {
91162306a36Sopenharmony_ci		switch (mode->scale) {
91262306a36Sopenharmony_ci		case SCALE_4CIFS:
91362306a36Sopenharmony_ci		case SCALE_4CIFSI:
91462306a36Sopenharmony_ci			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
91562306a36Sopenharmony_ci			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
91662306a36Sopenharmony_ci			break;
91762306a36Sopenharmony_ci		case SCALE_2CIFS:
91862306a36Sopenharmony_ci			linesPerFrame = NUM_LINES_2CIFS_NTSC;
91962306a36Sopenharmony_ci			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
92062306a36Sopenharmony_ci			break;
92162306a36Sopenharmony_ci		case SCALE_1CIFS:
92262306a36Sopenharmony_ci			linesPerFrame = NUM_LINES_1CIFS_NTSC;
92362306a36Sopenharmony_ci			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
92462306a36Sopenharmony_ci			break;
92562306a36Sopenharmony_ci		default:
92662306a36Sopenharmony_ci			break;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci	} else if (mode->format == FORMAT_PAL) {
92962306a36Sopenharmony_ci		switch (mode->scale) {
93062306a36Sopenharmony_ci		case SCALE_4CIFS:
93162306a36Sopenharmony_ci		case SCALE_4CIFSI:
93262306a36Sopenharmony_ci			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
93362306a36Sopenharmony_ci			pixelsPerLine = LINE_SZ_4CIFS_PAL;
93462306a36Sopenharmony_ci			break;
93562306a36Sopenharmony_ci		case SCALE_2CIFS:
93662306a36Sopenharmony_ci			linesPerFrame = NUM_LINES_2CIFS_PAL;
93762306a36Sopenharmony_ci			pixelsPerLine = LINE_SZ_2CIFS_PAL;
93862306a36Sopenharmony_ci			break;
93962306a36Sopenharmony_ci		case SCALE_1CIFS:
94062306a36Sopenharmony_ci			linesPerFrame = NUM_LINES_1CIFS_PAL;
94162306a36Sopenharmony_ci			pixelsPerLine = LINE_SZ_1CIFS_PAL;
94262306a36Sopenharmony_ci			break;
94362306a36Sopenharmony_ci		default:
94462306a36Sopenharmony_ci			break;
94562306a36Sopenharmony_ci		}
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	outImageSize = linesPerFrame * pixelsPerLine;
94862306a36Sopenharmony_ci	if ((mode->color & MASK_COLOR) != COLOR_Y8) {
94962306a36Sopenharmony_ci		/* 2 bytes/pixel if not monochrome */
95062306a36Sopenharmony_ci		outImageSize *= 2;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* total bytes to send including prefix and 4K padding;
95462306a36Sopenharmony_ci	   must be a multiple of USB_READ_SIZE */
95562306a36Sopenharmony_ci	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
95662306a36Sopenharmony_ci	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
95762306a36Sopenharmony_ci	/* if size not a multiple of USB_READ_SIZE */
95862306a36Sopenharmony_ci	if (usbInSize & ~mask_mult)
95962306a36Sopenharmony_ci		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
96062306a36Sopenharmony_ci	return usbInSize;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct device *dev = &sdev->udev->dev;
96662306a36Sopenharmony_ci	dev_info(dev, "------------------------------------------------\n");
96762306a36Sopenharmony_ci	dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
96862306a36Sopenharmony_ci	dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
96962306a36Sopenharmony_ci	dev_info(dev, "bright: 0x%x\n", mode->bright);
97062306a36Sopenharmony_ci	dev_info(dev, "------------------------------------------------\n");
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci/*
97462306a36Sopenharmony_ci * set mode is the function which controls the DSP.
97562306a36Sopenharmony_ci * the restart parameter in struct s2255_mode should be set whenever
97662306a36Sopenharmony_ci * the image size could change via color format, video system or image
97762306a36Sopenharmony_ci * size.
97862306a36Sopenharmony_ci * When the restart parameter is set, we sleep for ONE frame to allow the
97962306a36Sopenharmony_ci * DSP time to get the new frame
98062306a36Sopenharmony_ci */
98162306a36Sopenharmony_cistatic int s2255_set_mode(struct s2255_vc *vc,
98262306a36Sopenharmony_ci			  struct s2255_mode *mode)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	int res;
98562306a36Sopenharmony_ci	unsigned long chn_rev;
98662306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
98762306a36Sopenharmony_ci	int i;
98862306a36Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	mutex_lock(&dev->cmdlock);
99162306a36Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
99262306a36Sopenharmony_ci	dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx);
99362306a36Sopenharmony_ci	/* if JPEG, set the quality */
99462306a36Sopenharmony_ci	if ((mode->color & MASK_COLOR) == COLOR_JPG) {
99562306a36Sopenharmony_ci		mode->color &= ~MASK_COLOR;
99662306a36Sopenharmony_ci		mode->color |= COLOR_JPG;
99762306a36Sopenharmony_ci		mode->color &= ~MASK_JPG_QUALITY;
99862306a36Sopenharmony_ci		mode->color |= (vc->jpegqual << 8);
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci	/* save the mode */
100162306a36Sopenharmony_ci	vc->mode = *mode;
100262306a36Sopenharmony_ci	vc->req_image_size = get_transfer_size(mode);
100362306a36Sopenharmony_ci	dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size);
100462306a36Sopenharmony_ci	/* set the mode */
100562306a36Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
100662306a36Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
100762306a36Sopenharmony_ci	buffer[2] = CMD_SET_MODE;
100862306a36Sopenharmony_ci	for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++)
100962306a36Sopenharmony_ci		buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]);
101062306a36Sopenharmony_ci	vc->setmode_ready = 0;
101162306a36Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
101262306a36Sopenharmony_ci	if (debug)
101362306a36Sopenharmony_ci		s2255_print_cfg(dev, mode);
101462306a36Sopenharmony_ci	/* wait at least 3 frames before continuing */
101562306a36Sopenharmony_ci	if (mode->restart) {
101662306a36Sopenharmony_ci		wait_event_timeout(vc->wait_setmode,
101762306a36Sopenharmony_ci				   (vc->setmode_ready != 0),
101862306a36Sopenharmony_ci				   msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
101962306a36Sopenharmony_ci		if (vc->setmode_ready != 1) {
102062306a36Sopenharmony_ci			dprintk(dev, 0, "s2255: no set mode response\n");
102162306a36Sopenharmony_ci			res = -EFAULT;
102262306a36Sopenharmony_ci		}
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci	/* clear the restart flag */
102562306a36Sopenharmony_ci	vc->mode.restart = 0;
102662306a36Sopenharmony_ci	dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res);
102762306a36Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
102862306a36Sopenharmony_ci	return res;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	int res;
103462306a36Sopenharmony_ci	u32 chn_rev;
103562306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
103662306a36Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	mutex_lock(&dev->cmdlock);
103962306a36Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
104062306a36Sopenharmony_ci	dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx);
104162306a36Sopenharmony_ci	/* form the get vid status command */
104262306a36Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
104362306a36Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
104462306a36Sopenharmony_ci	buffer[2] = CMD_STATUS;
104562306a36Sopenharmony_ci	*pstatus = 0;
104662306a36Sopenharmony_ci	vc->vidstatus_ready = 0;
104762306a36Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
104862306a36Sopenharmony_ci	wait_event_timeout(vc->wait_vidstatus,
104962306a36Sopenharmony_ci			   (vc->vidstatus_ready != 0),
105062306a36Sopenharmony_ci			   msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
105162306a36Sopenharmony_ci	if (vc->vidstatus_ready != 1) {
105262306a36Sopenharmony_ci		dprintk(dev, 0, "s2255: no vidstatus response\n");
105362306a36Sopenharmony_ci		res = -EFAULT;
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci	*pstatus = vc->vidstatus;
105662306a36Sopenharmony_ci	dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus);
105762306a36Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
105862306a36Sopenharmony_ci	return res;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vq);
106462306a36Sopenharmony_ci	int j;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	vc->last_frame = -1;
106762306a36Sopenharmony_ci	vc->bad_payload = 0;
106862306a36Sopenharmony_ci	vc->cur_frame = 0;
106962306a36Sopenharmony_ci	vc->frame_count = 0;
107062306a36Sopenharmony_ci	for (j = 0; j < SYS_FRAMES; j++) {
107162306a36Sopenharmony_ci		vc->buffer.frame[j].ulState = S2255_READ_IDLE;
107262306a36Sopenharmony_ci		vc->buffer.frame[j].cur_size = 0;
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci	return s2255_start_acquire(vc);
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci/* abort streaming and wait for last buffer */
107862306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	struct s2255_vc *vc = vb2_get_drv_priv(vq);
108162306a36Sopenharmony_ci	struct s2255_buffer *buf, *node;
108262306a36Sopenharmony_ci	unsigned long flags;
108362306a36Sopenharmony_ci	(void) s2255_stop_acquire(vc);
108462306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
108562306a36Sopenharmony_ci	list_for_each_entry_safe(buf, node, &vc->buf_list, list) {
108662306a36Sopenharmony_ci		list_del(&buf->list);
108762306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
108862306a36Sopenharmony_ci		dprintk(vc->dev, 2, "[%p/%d] done\n",
108962306a36Sopenharmony_ci			buf, buf->vb.vb2_buf.index);
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
109762306a36Sopenharmony_ci	struct s2255_mode mode;
109862306a36Sopenharmony_ci	struct vb2_queue *q = &vc->vb_vidq;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/*
110162306a36Sopenharmony_ci	 * Changing the standard implies a format change, which is not allowed
110262306a36Sopenharmony_ci	 * while buffers for use with streaming have already been allocated.
110362306a36Sopenharmony_ci	 */
110462306a36Sopenharmony_ci	if (vb2_is_busy(q))
110562306a36Sopenharmony_ci		return -EBUSY;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	mode = vc->mode;
110862306a36Sopenharmony_ci	if (i & V4L2_STD_525_60) {
110962306a36Sopenharmony_ci		dprintk(vc->dev, 4, "%s 60 Hz\n", __func__);
111062306a36Sopenharmony_ci		/* if changing format, reset frame decimation/intervals */
111162306a36Sopenharmony_ci		if (mode.format != FORMAT_NTSC) {
111262306a36Sopenharmony_ci			mode.restart = 1;
111362306a36Sopenharmony_ci			mode.format = FORMAT_NTSC;
111462306a36Sopenharmony_ci			mode.fdec = FDEC_1;
111562306a36Sopenharmony_ci			vc->width = LINE_SZ_4CIFS_NTSC;
111662306a36Sopenharmony_ci			vc->height = NUM_LINES_4CIFS_NTSC * 2;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci	} else if (i & V4L2_STD_625_50) {
111962306a36Sopenharmony_ci		dprintk(vc->dev, 4, "%s 50 Hz\n", __func__);
112062306a36Sopenharmony_ci		if (mode.format != FORMAT_PAL) {
112162306a36Sopenharmony_ci			mode.restart = 1;
112262306a36Sopenharmony_ci			mode.format = FORMAT_PAL;
112362306a36Sopenharmony_ci			mode.fdec = FDEC_1;
112462306a36Sopenharmony_ci			vc->width = LINE_SZ_4CIFS_PAL;
112562306a36Sopenharmony_ci			vc->height = NUM_LINES_4CIFS_PAL * 2;
112662306a36Sopenharmony_ci		}
112762306a36Sopenharmony_ci	} else
112862306a36Sopenharmony_ci		return -EINVAL;
112962306a36Sopenharmony_ci	vc->std = i;
113062306a36Sopenharmony_ci	if (mode.restart)
113162306a36Sopenharmony_ci		s2255_set_mode(vc, &mode);
113262306a36Sopenharmony_ci	return 0;
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	*i = vc->std;
114062306a36Sopenharmony_ci	return 0;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci/* Sensoray 2255 is a multiple channel capture device.
114462306a36Sopenharmony_ci   It does not have a "crossbar" of inputs.
114562306a36Sopenharmony_ci   We use one V4L device per channel. The user must
114662306a36Sopenharmony_ci   be aware that certain combinations are not allowed.
114762306a36Sopenharmony_ci   For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
114862306a36Sopenharmony_ci   at once in color(you can do full fps on 4 channels with greyscale.
114962306a36Sopenharmony_ci*/
115062306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
115162306a36Sopenharmony_ci			     struct v4l2_input *inp)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
115462306a36Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
115562306a36Sopenharmony_ci	u32 status = 0;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (inp->index != 0)
115862306a36Sopenharmony_ci		return -EINVAL;
115962306a36Sopenharmony_ci	inp->type = V4L2_INPUT_TYPE_CAMERA;
116062306a36Sopenharmony_ci	inp->std = S2255_NORMS;
116162306a36Sopenharmony_ci	inp->status = 0;
116262306a36Sopenharmony_ci	if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
116362306a36Sopenharmony_ci		int rc;
116462306a36Sopenharmony_ci		rc = s2255_cmd_status(vc, &status);
116562306a36Sopenharmony_ci		dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n",
116662306a36Sopenharmony_ci			rc, status);
116762306a36Sopenharmony_ci		if (rc == 0)
116862306a36Sopenharmony_ci			inp->status =  (status & 0x01) ? 0
116962306a36Sopenharmony_ci				: V4L2_IN_ST_NO_SIGNAL;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci	switch (dev->pid) {
117262306a36Sopenharmony_ci	case 0x2255:
117362306a36Sopenharmony_ci	default:
117462306a36Sopenharmony_ci		strscpy(inp->name, "Composite", sizeof(inp->name));
117562306a36Sopenharmony_ci		break;
117662306a36Sopenharmony_ci	case 0x2257:
117762306a36Sopenharmony_ci		strscpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video",
117862306a36Sopenharmony_ci			sizeof(inp->name));
117962306a36Sopenharmony_ci		break;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci	return 0;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	*i = 0;
118762306a36Sopenharmony_ci	return 0;
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	if (i > 0)
119262306a36Sopenharmony_ci		return -EINVAL;
119362306a36Sopenharmony_ci	return 0;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	struct s2255_vc *vc =
119962306a36Sopenharmony_ci		container_of(ctrl->handler, struct s2255_vc, hdl);
120062306a36Sopenharmony_ci	struct s2255_mode mode;
120162306a36Sopenharmony_ci	mode = vc->mode;
120262306a36Sopenharmony_ci	/* update the mode to the corresponding value */
120362306a36Sopenharmony_ci	switch (ctrl->id) {
120462306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
120562306a36Sopenharmony_ci		mode.bright = ctrl->val;
120662306a36Sopenharmony_ci		break;
120762306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
120862306a36Sopenharmony_ci		mode.contrast = ctrl->val;
120962306a36Sopenharmony_ci		break;
121062306a36Sopenharmony_ci	case V4L2_CID_HUE:
121162306a36Sopenharmony_ci		mode.hue = ctrl->val;
121262306a36Sopenharmony_ci		break;
121362306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
121462306a36Sopenharmony_ci		mode.saturation = ctrl->val;
121562306a36Sopenharmony_ci		break;
121662306a36Sopenharmony_ci	case V4L2_CID_S2255_COLORFILTER:
121762306a36Sopenharmony_ci		mode.color &= ~MASK_INPUT_TYPE;
121862306a36Sopenharmony_ci		mode.color |= !ctrl->val << 16;
121962306a36Sopenharmony_ci		break;
122062306a36Sopenharmony_ci	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
122162306a36Sopenharmony_ci		vc->jpegqual = ctrl->val;
122262306a36Sopenharmony_ci		return 0;
122362306a36Sopenharmony_ci	default:
122462306a36Sopenharmony_ci		return -EINVAL;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci	mode.restart = 0;
122762306a36Sopenharmony_ci	/* set mode here.  Note: stream does not need restarted.
122862306a36Sopenharmony_ci	   some V4L programs restart stream unnecessarily
122962306a36Sopenharmony_ci	   after a s_crtl.
123062306a36Sopenharmony_ci	*/
123162306a36Sopenharmony_ci	s2255_set_mode(vc, &mode);
123262306a36Sopenharmony_ci	return 0;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic int vidioc_g_jpegcomp(struct file *file, void *priv,
123662306a36Sopenharmony_ci			 struct v4l2_jpegcompression *jc)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	memset(jc, 0, sizeof(*jc));
124162306a36Sopenharmony_ci	jc->quality = vc->jpegqual;
124262306a36Sopenharmony_ci	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
124362306a36Sopenharmony_ci	return 0;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic int vidioc_s_jpegcomp(struct file *file, void *priv,
124762306a36Sopenharmony_ci			 const struct v4l2_jpegcompression *jc)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (jc->quality < 0 || jc->quality > 100)
125262306a36Sopenharmony_ci		return -EINVAL;
125362306a36Sopenharmony_ci	v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality);
125462306a36Sopenharmony_ci	dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
125562306a36Sopenharmony_ci	return 0;
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic int vidioc_g_parm(struct file *file, void *priv,
125962306a36Sopenharmony_ci			 struct v4l2_streamparm *sp)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	__u32 def_num, def_dem;
126262306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
126562306a36Sopenharmony_ci		return -EINVAL;
126662306a36Sopenharmony_ci	sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
126762306a36Sopenharmony_ci	sp->parm.capture.capturemode = vc->cap_parm.capturemode;
126862306a36Sopenharmony_ci	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
126962306a36Sopenharmony_ci	def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000;
127062306a36Sopenharmony_ci	def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000;
127162306a36Sopenharmony_ci	sp->parm.capture.timeperframe.denominator = def_dem;
127262306a36Sopenharmony_ci	switch (vc->mode.fdec) {
127362306a36Sopenharmony_ci	default:
127462306a36Sopenharmony_ci	case FDEC_1:
127562306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num;
127662306a36Sopenharmony_ci		break;
127762306a36Sopenharmony_ci	case FDEC_2:
127862306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 2;
127962306a36Sopenharmony_ci		break;
128062306a36Sopenharmony_ci	case FDEC_3:
128162306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 3;
128262306a36Sopenharmony_ci		break;
128362306a36Sopenharmony_ci	case FDEC_5:
128462306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 5;
128562306a36Sopenharmony_ci		break;
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n",
128862306a36Sopenharmony_ci		__func__,
128962306a36Sopenharmony_ci		sp->parm.capture.capturemode,
129062306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator,
129162306a36Sopenharmony_ci		sp->parm.capture.timeperframe.denominator);
129262306a36Sopenharmony_ci	return 0;
129362306a36Sopenharmony_ci}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic int vidioc_s_parm(struct file *file, void *priv,
129662306a36Sopenharmony_ci			 struct v4l2_streamparm *sp)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
129962306a36Sopenharmony_ci	struct s2255_mode mode;
130062306a36Sopenharmony_ci	int fdec = FDEC_1;
130162306a36Sopenharmony_ci	__u32 def_num, def_dem;
130262306a36Sopenharmony_ci	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
130362306a36Sopenharmony_ci		return -EINVAL;
130462306a36Sopenharmony_ci	mode = vc->mode;
130562306a36Sopenharmony_ci	/* high quality capture mode requires a stream restart */
130662306a36Sopenharmony_ci	if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode)
130762306a36Sopenharmony_ci	    && vb2_is_streaming(&vc->vb_vidq))
130862306a36Sopenharmony_ci		return -EBUSY;
130962306a36Sopenharmony_ci	def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
131062306a36Sopenharmony_ci	def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
131162306a36Sopenharmony_ci	if (def_dem != sp->parm.capture.timeperframe.denominator)
131262306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num;
131362306a36Sopenharmony_ci	else if (sp->parm.capture.timeperframe.numerator <= def_num)
131462306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num;
131562306a36Sopenharmony_ci	else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
131662306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 2;
131762306a36Sopenharmony_ci		fdec = FDEC_2;
131862306a36Sopenharmony_ci	} else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
131962306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 3;
132062306a36Sopenharmony_ci		fdec = FDEC_3;
132162306a36Sopenharmony_ci	} else {
132262306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator = def_num * 5;
132362306a36Sopenharmony_ci		fdec = FDEC_5;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci	mode.fdec = fdec;
132662306a36Sopenharmony_ci	sp->parm.capture.timeperframe.denominator = def_dem;
132762306a36Sopenharmony_ci	sp->parm.capture.readbuffers = S2255_MIN_BUFS;
132862306a36Sopenharmony_ci	s2255_set_mode(vc, &mode);
132962306a36Sopenharmony_ci	dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
133062306a36Sopenharmony_ci		__func__,
133162306a36Sopenharmony_ci		sp->parm.capture.capturemode,
133262306a36Sopenharmony_ci		sp->parm.capture.timeperframe.numerator,
133362306a36Sopenharmony_ci		sp->parm.capture.timeperframe.denominator, fdec);
133462306a36Sopenharmony_ci	return 0;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci#define NUM_SIZE_ENUMS 3
133862306a36Sopenharmony_cistatic const struct v4l2_frmsize_discrete ntsc_sizes[] = {
133962306a36Sopenharmony_ci	{ 640, 480 },
134062306a36Sopenharmony_ci	{ 640, 240 },
134162306a36Sopenharmony_ci	{ 320, 240 },
134262306a36Sopenharmony_ci};
134362306a36Sopenharmony_cistatic const struct v4l2_frmsize_discrete pal_sizes[] = {
134462306a36Sopenharmony_ci	{ 704, 576 },
134562306a36Sopenharmony_ci	{ 704, 288 },
134662306a36Sopenharmony_ci	{ 352, 288 },
134762306a36Sopenharmony_ci};
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int vidioc_enum_framesizes(struct file *file, void *priv,
135062306a36Sopenharmony_ci			    struct v4l2_frmsizeenum *fe)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
135362306a36Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
135462306a36Sopenharmony_ci	const struct s2255_fmt *fmt;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	if (fe->index >= NUM_SIZE_ENUMS)
135762306a36Sopenharmony_ci		return -EINVAL;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	fmt = format_by_fourcc(fe->pixel_format);
136062306a36Sopenharmony_ci	if (fmt == NULL)
136162306a36Sopenharmony_ci		return -EINVAL;
136262306a36Sopenharmony_ci	fe->type = V4L2_FRMSIZE_TYPE_DISCRETE;
136362306a36Sopenharmony_ci	fe->discrete = is_ntsc ?  ntsc_sizes[fe->index] : pal_sizes[fe->index];
136462306a36Sopenharmony_ci	return 0;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int vidioc_enum_frameintervals(struct file *file, void *priv,
136862306a36Sopenharmony_ci			    struct v4l2_frmivalenum *fe)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
137162306a36Sopenharmony_ci	const struct s2255_fmt *fmt;
137262306a36Sopenharmony_ci	const struct v4l2_frmsize_discrete *sizes;
137362306a36Sopenharmony_ci	int is_ntsc = vc->std & V4L2_STD_525_60;
137462306a36Sopenharmony_ci#define NUM_FRAME_ENUMS 4
137562306a36Sopenharmony_ci	int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
137662306a36Sopenharmony_ci	int i;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	if (fe->index >= NUM_FRAME_ENUMS)
137962306a36Sopenharmony_ci		return -EINVAL;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	fmt = format_by_fourcc(fe->pixel_format);
138262306a36Sopenharmony_ci	if (fmt == NULL)
138362306a36Sopenharmony_ci		return -EINVAL;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	sizes = is_ntsc ? ntsc_sizes : pal_sizes;
138662306a36Sopenharmony_ci	for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++)
138762306a36Sopenharmony_ci		if (fe->width == sizes->width &&
138862306a36Sopenharmony_ci		    fe->height == sizes->height)
138962306a36Sopenharmony_ci			break;
139062306a36Sopenharmony_ci	if (i == NUM_SIZE_ENUMS)
139162306a36Sopenharmony_ci		return -EINVAL;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
139462306a36Sopenharmony_ci	fe->discrete.denominator = is_ntsc ? 30000 : 25000;
139562306a36Sopenharmony_ci	fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
139662306a36Sopenharmony_ci	dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__,
139762306a36Sopenharmony_ci		fe->discrete.numerator,
139862306a36Sopenharmony_ci		fe->discrete.denominator);
139962306a36Sopenharmony_ci	return 0;
140062306a36Sopenharmony_ci}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_cistatic int s2255_open(struct file *file)
140362306a36Sopenharmony_ci{
140462306a36Sopenharmony_ci	struct s2255_vc *vc = video_drvdata(file);
140562306a36Sopenharmony_ci	struct s2255_dev *dev = vc->dev;
140662306a36Sopenharmony_ci	int state;
140762306a36Sopenharmony_ci	int rc = 0;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	rc = v4l2_fh_open(file);
141062306a36Sopenharmony_ci	if (rc != 0)
141162306a36Sopenharmony_ci		return rc;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	dprintk(dev, 1, "s2255: %s\n", __func__);
141462306a36Sopenharmony_ci	state = atomic_read(&dev->fw_data->fw_state);
141562306a36Sopenharmony_ci	switch (state) {
141662306a36Sopenharmony_ci	case S2255_FW_DISCONNECTING:
141762306a36Sopenharmony_ci		return -ENODEV;
141862306a36Sopenharmony_ci	case S2255_FW_FAILED:
141962306a36Sopenharmony_ci		s2255_dev_err(&dev->udev->dev,
142062306a36Sopenharmony_ci			"firmware load failed. retrying.\n");
142162306a36Sopenharmony_ci		s2255_fwload_start(dev);
142262306a36Sopenharmony_ci		wait_event_timeout(dev->fw_data->wait_fw,
142362306a36Sopenharmony_ci				   ((atomic_read(&dev->fw_data->fw_state)
142462306a36Sopenharmony_ci				     == S2255_FW_SUCCESS) ||
142562306a36Sopenharmony_ci				    (atomic_read(&dev->fw_data->fw_state)
142662306a36Sopenharmony_ci				     == S2255_FW_DISCONNECTING)),
142762306a36Sopenharmony_ci				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
142862306a36Sopenharmony_ci		/* state may have changed, re-read */
142962306a36Sopenharmony_ci		state = atomic_read(&dev->fw_data->fw_state);
143062306a36Sopenharmony_ci		break;
143162306a36Sopenharmony_ci	case S2255_FW_NOTLOADED:
143262306a36Sopenharmony_ci	case S2255_FW_LOADED_DSPWAIT:
143362306a36Sopenharmony_ci		/* give S2255_LOAD_TIMEOUT time for firmware to load in case
143462306a36Sopenharmony_ci		   driver loaded and then device immediately opened */
143562306a36Sopenharmony_ci		pr_info("%s waiting for firmware load\n", __func__);
143662306a36Sopenharmony_ci		wait_event_timeout(dev->fw_data->wait_fw,
143762306a36Sopenharmony_ci				   ((atomic_read(&dev->fw_data->fw_state)
143862306a36Sopenharmony_ci				     == S2255_FW_SUCCESS) ||
143962306a36Sopenharmony_ci				    (atomic_read(&dev->fw_data->fw_state)
144062306a36Sopenharmony_ci				     == S2255_FW_DISCONNECTING)),
144162306a36Sopenharmony_ci				   msecs_to_jiffies(S2255_LOAD_TIMEOUT));
144262306a36Sopenharmony_ci		/* state may have changed, re-read */
144362306a36Sopenharmony_ci		state = atomic_read(&dev->fw_data->fw_state);
144462306a36Sopenharmony_ci		break;
144562306a36Sopenharmony_ci	case S2255_FW_SUCCESS:
144662306a36Sopenharmony_ci	default:
144762306a36Sopenharmony_ci		break;
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci	/* state may have changed in above switch statement */
145062306a36Sopenharmony_ci	switch (state) {
145162306a36Sopenharmony_ci	case S2255_FW_SUCCESS:
145262306a36Sopenharmony_ci		break;
145362306a36Sopenharmony_ci	case S2255_FW_FAILED:
145462306a36Sopenharmony_ci		pr_info("2255 firmware load failed.\n");
145562306a36Sopenharmony_ci		return -ENODEV;
145662306a36Sopenharmony_ci	case S2255_FW_DISCONNECTING:
145762306a36Sopenharmony_ci		pr_info("%s: disconnecting\n", __func__);
145862306a36Sopenharmony_ci		return -ENODEV;
145962306a36Sopenharmony_ci	case S2255_FW_LOADED_DSPWAIT:
146062306a36Sopenharmony_ci	case S2255_FW_NOTLOADED:
146162306a36Sopenharmony_ci		pr_info("%s: firmware not loaded, please retry\n",
146262306a36Sopenharmony_ci			__func__);
146362306a36Sopenharmony_ci		/*
146462306a36Sopenharmony_ci		 * Timeout on firmware load means device unusable.
146562306a36Sopenharmony_ci		 * Set firmware failure state.
146662306a36Sopenharmony_ci		 * On next s2255_open the firmware will be reloaded.
146762306a36Sopenharmony_ci		 */
146862306a36Sopenharmony_ci		atomic_set(&dev->fw_data->fw_state,
146962306a36Sopenharmony_ci			   S2255_FW_FAILED);
147062306a36Sopenharmony_ci		return -EAGAIN;
147162306a36Sopenharmony_ci	default:
147262306a36Sopenharmony_ci		pr_info("%s: unknown state\n", __func__);
147362306a36Sopenharmony_ci		return -EFAULT;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci	if (!vc->configured) {
147662306a36Sopenharmony_ci		/* configure channel to default state */
147762306a36Sopenharmony_ci		vc->fmt = &formats[0];
147862306a36Sopenharmony_ci		s2255_set_mode(vc, &vc->mode);
147962306a36Sopenharmony_ci		vc->configured = 1;
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci	return 0;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic void s2255_destroy(struct s2255_dev *dev)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	dprintk(dev, 1, "%s", __func__);
148762306a36Sopenharmony_ci	/* board shutdown stops the read pipe if it is running */
148862306a36Sopenharmony_ci	s2255_board_shutdown(dev);
148962306a36Sopenharmony_ci	/* make sure firmware still not trying to load */
149062306a36Sopenharmony_ci	timer_shutdown_sync(&dev->timer);  /* only started in .probe and .open */
149162306a36Sopenharmony_ci	if (dev->fw_data->fw_urb) {
149262306a36Sopenharmony_ci		usb_kill_urb(dev->fw_data->fw_urb);
149362306a36Sopenharmony_ci		usb_free_urb(dev->fw_data->fw_urb);
149462306a36Sopenharmony_ci		dev->fw_data->fw_urb = NULL;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci	release_firmware(dev->fw_data->fw);
149762306a36Sopenharmony_ci	kfree(dev->fw_data->pfw_data);
149862306a36Sopenharmony_ci	kfree(dev->fw_data);
149962306a36Sopenharmony_ci	/* reset the DSP so firmware can be reloaded next time */
150062306a36Sopenharmony_ci	s2255_reset_dsppower(dev);
150162306a36Sopenharmony_ci	mutex_destroy(&dev->lock);
150262306a36Sopenharmony_ci	usb_put_dev(dev->udev);
150362306a36Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
150462306a36Sopenharmony_ci	kfree(dev->cmdbuf);
150562306a36Sopenharmony_ci	kfree(dev);
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic const struct v4l2_file_operations s2255_fops_v4l = {
150962306a36Sopenharmony_ci	.owner = THIS_MODULE,
151062306a36Sopenharmony_ci	.open = s2255_open,
151162306a36Sopenharmony_ci	.release = vb2_fop_release,
151262306a36Sopenharmony_ci	.poll = vb2_fop_poll,
151362306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,	/* V4L2 ioctl handler */
151462306a36Sopenharmony_ci	.mmap = vb2_fop_mmap,
151562306a36Sopenharmony_ci	.read = vb2_fop_read,
151662306a36Sopenharmony_ci};
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops s2255_ioctl_ops = {
151962306a36Sopenharmony_ci	.vidioc_querycap = vidioc_querycap,
152062306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
152162306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
152262306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
152362306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
152462306a36Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
152562306a36Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
152662306a36Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
152762306a36Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
152862306a36Sopenharmony_ci	.vidioc_s_std = vidioc_s_std,
152962306a36Sopenharmony_ci	.vidioc_g_std = vidioc_g_std,
153062306a36Sopenharmony_ci	.vidioc_enum_input = vidioc_enum_input,
153162306a36Sopenharmony_ci	.vidioc_g_input = vidioc_g_input,
153262306a36Sopenharmony_ci	.vidioc_s_input = vidioc_s_input,
153362306a36Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
153462306a36Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
153562306a36Sopenharmony_ci	.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
153662306a36Sopenharmony_ci	.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
153762306a36Sopenharmony_ci	.vidioc_s_parm = vidioc_s_parm,
153862306a36Sopenharmony_ci	.vidioc_g_parm = vidioc_g_parm,
153962306a36Sopenharmony_ci	.vidioc_enum_framesizes = vidioc_enum_framesizes,
154062306a36Sopenharmony_ci	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
154162306a36Sopenharmony_ci	.vidioc_log_status  = v4l2_ctrl_log_status,
154262306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
154362306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
154462306a36Sopenharmony_ci};
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_cistatic void s2255_video_device_release(struct video_device *vdev)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
154962306a36Sopenharmony_ci	struct s2255_vc *vc =
155062306a36Sopenharmony_ci		container_of(vdev, struct s2255_vc, vdev);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	dprintk(dev, 4, "%s, chnls: %d\n", __func__,
155362306a36Sopenharmony_ci		atomic_read(&dev->num_channels));
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&vc->hdl);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	if (atomic_dec_and_test(&dev->num_channels))
155862306a36Sopenharmony_ci		s2255_destroy(dev);
155962306a36Sopenharmony_ci	return;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_cistatic const struct video_device template = {
156362306a36Sopenharmony_ci	.name = "s2255v",
156462306a36Sopenharmony_ci	.fops = &s2255_fops_v4l,
156562306a36Sopenharmony_ci	.ioctl_ops = &s2255_ioctl_ops,
156662306a36Sopenharmony_ci	.release = s2255_video_device_release,
156762306a36Sopenharmony_ci	.tvnorms = S2255_NORMS,
156862306a36Sopenharmony_ci};
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops s2255_ctrl_ops = {
157162306a36Sopenharmony_ci	.s_ctrl = s2255_s_ctrl,
157262306a36Sopenharmony_ci};
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_cistatic const struct v4l2_ctrl_config color_filter_ctrl = {
157562306a36Sopenharmony_ci	.ops = &s2255_ctrl_ops,
157662306a36Sopenharmony_ci	.name = "Color Filter",
157762306a36Sopenharmony_ci	.id = V4L2_CID_S2255_COLORFILTER,
157862306a36Sopenharmony_ci	.type = V4L2_CTRL_TYPE_BOOLEAN,
157962306a36Sopenharmony_ci	.max = 1,
158062306a36Sopenharmony_ci	.step = 1,
158162306a36Sopenharmony_ci	.def = 1,
158262306a36Sopenharmony_ci};
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_cistatic int s2255_probe_v4l(struct s2255_dev *dev)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	int ret;
158762306a36Sopenharmony_ci	int i;
158862306a36Sopenharmony_ci	int cur_nr = video_nr;
158962306a36Sopenharmony_ci	struct s2255_vc *vc;
159062306a36Sopenharmony_ci	struct vb2_queue *q;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
159362306a36Sopenharmony_ci	if (ret)
159462306a36Sopenharmony_ci		return ret;
159562306a36Sopenharmony_ci	/* initialize all video 4 linux */
159662306a36Sopenharmony_ci	/* register 4 video devices */
159762306a36Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
159862306a36Sopenharmony_ci		vc = &dev->vc[i];
159962306a36Sopenharmony_ci		INIT_LIST_HEAD(&vc->buf_list);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci		v4l2_ctrl_handler_init(&vc->hdl, 6);
160262306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
160362306a36Sopenharmony_ci				V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT);
160462306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
160562306a36Sopenharmony_ci				V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST);
160662306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
160762306a36Sopenharmony_ci				V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION);
160862306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
160962306a36Sopenharmony_ci				V4L2_CID_HUE, 0, 255, 1, DEF_HUE);
161062306a36Sopenharmony_ci		vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl,
161162306a36Sopenharmony_ci				&s2255_ctrl_ops,
161262306a36Sopenharmony_ci				V4L2_CID_JPEG_COMPRESSION_QUALITY,
161362306a36Sopenharmony_ci				0, 100, 1, S2255_DEF_JPEG_QUAL);
161462306a36Sopenharmony_ci		if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER &&
161562306a36Sopenharmony_ci		    (dev->pid != 0x2257 || vc->idx <= 1))
161662306a36Sopenharmony_ci			v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl,
161762306a36Sopenharmony_ci					     NULL);
161862306a36Sopenharmony_ci		if (vc->hdl.error) {
161962306a36Sopenharmony_ci			ret = vc->hdl.error;
162062306a36Sopenharmony_ci			v4l2_ctrl_handler_free(&vc->hdl);
162162306a36Sopenharmony_ci			dev_err(&dev->udev->dev, "couldn't register control\n");
162262306a36Sopenharmony_ci			break;
162362306a36Sopenharmony_ci		}
162462306a36Sopenharmony_ci		q = &vc->vb_vidq;
162562306a36Sopenharmony_ci		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
162662306a36Sopenharmony_ci		q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR;
162762306a36Sopenharmony_ci		q->drv_priv = vc;
162862306a36Sopenharmony_ci		q->lock = &vc->vb_lock;
162962306a36Sopenharmony_ci		q->buf_struct_size = sizeof(struct s2255_buffer);
163062306a36Sopenharmony_ci		q->mem_ops = &vb2_vmalloc_memops;
163162306a36Sopenharmony_ci		q->ops = &s2255_video_qops;
163262306a36Sopenharmony_ci		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
163362306a36Sopenharmony_ci		ret = vb2_queue_init(q);
163462306a36Sopenharmony_ci		if (ret != 0) {
163562306a36Sopenharmony_ci			dev_err(&dev->udev->dev,
163662306a36Sopenharmony_ci				"%s vb2_queue_init 0x%x\n", __func__, ret);
163762306a36Sopenharmony_ci			break;
163862306a36Sopenharmony_ci		}
163962306a36Sopenharmony_ci		/* register video devices */
164062306a36Sopenharmony_ci		vc->vdev = template;
164162306a36Sopenharmony_ci		vc->vdev.queue = q;
164262306a36Sopenharmony_ci		vc->vdev.ctrl_handler = &vc->hdl;
164362306a36Sopenharmony_ci		vc->vdev.lock = &dev->lock;
164462306a36Sopenharmony_ci		vc->vdev.v4l2_dev = &dev->v4l2_dev;
164562306a36Sopenharmony_ci		vc->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
164662306a36Sopenharmony_ci				       V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
164762306a36Sopenharmony_ci		video_set_drvdata(&vc->vdev, vc);
164862306a36Sopenharmony_ci		if (video_nr == -1)
164962306a36Sopenharmony_ci			ret = video_register_device(&vc->vdev,
165062306a36Sopenharmony_ci						    VFL_TYPE_VIDEO,
165162306a36Sopenharmony_ci						    video_nr);
165262306a36Sopenharmony_ci		else
165362306a36Sopenharmony_ci			ret = video_register_device(&vc->vdev,
165462306a36Sopenharmony_ci						    VFL_TYPE_VIDEO,
165562306a36Sopenharmony_ci						    cur_nr + i);
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci		if (ret) {
165862306a36Sopenharmony_ci			dev_err(&dev->udev->dev,
165962306a36Sopenharmony_ci				"failed to register video device!\n");
166062306a36Sopenharmony_ci			break;
166162306a36Sopenharmony_ci		}
166262306a36Sopenharmony_ci		atomic_inc(&dev->num_channels);
166362306a36Sopenharmony_ci		v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
166462306a36Sopenharmony_ci			  video_device_node_name(&vc->vdev));
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci	pr_info("Sensoray 2255 V4L driver Revision: %s\n",
166862306a36Sopenharmony_ci		S2255_VERSION);
166962306a36Sopenharmony_ci	/* if no channels registered, return error and probe will fail*/
167062306a36Sopenharmony_ci	if (atomic_read(&dev->num_channels) == 0) {
167162306a36Sopenharmony_ci		v4l2_device_unregister(&dev->v4l2_dev);
167262306a36Sopenharmony_ci		return ret;
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci	if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
167562306a36Sopenharmony_ci		pr_warn("s2255: Not all channels available.\n");
167662306a36Sopenharmony_ci	return 0;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci/* this function moves the usb stream read pipe data
168062306a36Sopenharmony_ci * into the system buffers.
168162306a36Sopenharmony_ci * returns 0 on success, EAGAIN if more data to process( call this
168262306a36Sopenharmony_ci * function again).
168362306a36Sopenharmony_ci *
168462306a36Sopenharmony_ci * Received frame structure:
168562306a36Sopenharmony_ci * bytes 0-3:  marker : 0x2255DA4AL (S2255_MARKER_FRAME)
168662306a36Sopenharmony_ci * bytes 4-7:  channel: 0-3
168762306a36Sopenharmony_ci * bytes 8-11: payload size:  size of the frame
168862306a36Sopenharmony_ci * bytes 12-payloadsize+12:  frame data
168962306a36Sopenharmony_ci */
169062306a36Sopenharmony_cistatic int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	char *pdest;
169362306a36Sopenharmony_ci	u32 offset = 0;
169462306a36Sopenharmony_ci	int bframe = 0;
169562306a36Sopenharmony_ci	char *psrc;
169662306a36Sopenharmony_ci	unsigned long copy_size;
169762306a36Sopenharmony_ci	unsigned long size;
169862306a36Sopenharmony_ci	s32 idx = -1;
169962306a36Sopenharmony_ci	struct s2255_framei *frm;
170062306a36Sopenharmony_ci	unsigned char *pdata;
170162306a36Sopenharmony_ci	struct s2255_vc *vc;
170262306a36Sopenharmony_ci	dprintk(dev, 100, "buffer to user\n");
170362306a36Sopenharmony_ci	vc = &dev->vc[dev->cc];
170462306a36Sopenharmony_ci	idx = vc->cur_frame;
170562306a36Sopenharmony_ci	frm = &vc->buffer.frame[idx];
170662306a36Sopenharmony_ci	if (frm->ulState == S2255_READ_IDLE) {
170762306a36Sopenharmony_ci		int jj;
170862306a36Sopenharmony_ci		unsigned int cc;
170962306a36Sopenharmony_ci		__le32 *pdword; /*data from dsp is little endian */
171062306a36Sopenharmony_ci		int payload;
171162306a36Sopenharmony_ci		/* search for marker codes */
171262306a36Sopenharmony_ci		pdata = (unsigned char *)pipe_info->transfer_buffer;
171362306a36Sopenharmony_ci		pdword = (__le32 *)pdata;
171462306a36Sopenharmony_ci		for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
171562306a36Sopenharmony_ci			switch (*pdword) {
171662306a36Sopenharmony_ci			case S2255_MARKER_FRAME:
171762306a36Sopenharmony_ci				dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
171862306a36Sopenharmony_ci					jj, pdata[0], pdata[1]);
171962306a36Sopenharmony_ci				offset = jj + PREFIX_SIZE;
172062306a36Sopenharmony_ci				bframe = 1;
172162306a36Sopenharmony_ci				cc = le32_to_cpu(pdword[1]);
172262306a36Sopenharmony_ci				if (cc >= MAX_CHANNELS) {
172362306a36Sopenharmony_ci					dprintk(dev, 0,
172462306a36Sopenharmony_ci						"bad channel\n");
172562306a36Sopenharmony_ci					return -EINVAL;
172662306a36Sopenharmony_ci				}
172762306a36Sopenharmony_ci				/* reverse it */
172862306a36Sopenharmony_ci				dev->cc = G_chnmap[cc];
172962306a36Sopenharmony_ci				vc = &dev->vc[dev->cc];
173062306a36Sopenharmony_ci				payload =  le32_to_cpu(pdword[3]);
173162306a36Sopenharmony_ci				if (payload > vc->req_image_size) {
173262306a36Sopenharmony_ci					vc->bad_payload++;
173362306a36Sopenharmony_ci					/* discard the bad frame */
173462306a36Sopenharmony_ci					return -EINVAL;
173562306a36Sopenharmony_ci				}
173662306a36Sopenharmony_ci				vc->pkt_size = payload;
173762306a36Sopenharmony_ci				vc->jpg_size = le32_to_cpu(pdword[4]);
173862306a36Sopenharmony_ci				break;
173962306a36Sopenharmony_ci			case S2255_MARKER_RESPONSE:
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci				pdata += DEF_USB_BLOCK;
174262306a36Sopenharmony_ci				jj += DEF_USB_BLOCK;
174362306a36Sopenharmony_ci				if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
174462306a36Sopenharmony_ci					break;
174562306a36Sopenharmony_ci				cc = G_chnmap[le32_to_cpu(pdword[1])];
174662306a36Sopenharmony_ci				if (cc >= MAX_CHANNELS)
174762306a36Sopenharmony_ci					break;
174862306a36Sopenharmony_ci				vc = &dev->vc[cc];
174962306a36Sopenharmony_ci				switch (pdword[2]) {
175062306a36Sopenharmony_ci				case S2255_RESPONSE_SETMODE:
175162306a36Sopenharmony_ci					/* check if channel valid */
175262306a36Sopenharmony_ci					/* set mode ready */
175362306a36Sopenharmony_ci					vc->setmode_ready = 1;
175462306a36Sopenharmony_ci					wake_up(&vc->wait_setmode);
175562306a36Sopenharmony_ci					dprintk(dev, 5, "setmode rdy %d\n", cc);
175662306a36Sopenharmony_ci					break;
175762306a36Sopenharmony_ci				case S2255_RESPONSE_FW:
175862306a36Sopenharmony_ci					dev->chn_ready |= (1 << cc);
175962306a36Sopenharmony_ci					if ((dev->chn_ready & 0x0f) != 0x0f)
176062306a36Sopenharmony_ci						break;
176162306a36Sopenharmony_ci					/* all channels ready */
176262306a36Sopenharmony_ci					pr_info("s2255: fw loaded\n");
176362306a36Sopenharmony_ci					atomic_set(&dev->fw_data->fw_state,
176462306a36Sopenharmony_ci						   S2255_FW_SUCCESS);
176562306a36Sopenharmony_ci					wake_up(&dev->fw_data->wait_fw);
176662306a36Sopenharmony_ci					break;
176762306a36Sopenharmony_ci				case S2255_RESPONSE_STATUS:
176862306a36Sopenharmony_ci					vc->vidstatus = le32_to_cpu(pdword[3]);
176962306a36Sopenharmony_ci					vc->vidstatus_ready = 1;
177062306a36Sopenharmony_ci					wake_up(&vc->wait_vidstatus);
177162306a36Sopenharmony_ci					dprintk(dev, 5, "vstat %x chan %d\n",
177262306a36Sopenharmony_ci						le32_to_cpu(pdword[3]), cc);
177362306a36Sopenharmony_ci					break;
177462306a36Sopenharmony_ci				default:
177562306a36Sopenharmony_ci					pr_info("s2255 unknown resp\n");
177662306a36Sopenharmony_ci				}
177762306a36Sopenharmony_ci				pdata++;
177862306a36Sopenharmony_ci				break;
177962306a36Sopenharmony_ci			default:
178062306a36Sopenharmony_ci				pdata++;
178162306a36Sopenharmony_ci				break;
178262306a36Sopenharmony_ci			}
178362306a36Sopenharmony_ci			if (bframe)
178462306a36Sopenharmony_ci				break;
178562306a36Sopenharmony_ci		} /* for */
178662306a36Sopenharmony_ci		if (!bframe)
178762306a36Sopenharmony_ci			return -EINVAL;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci	vc = &dev->vc[dev->cc];
179062306a36Sopenharmony_ci	idx = vc->cur_frame;
179162306a36Sopenharmony_ci	frm = &vc->buffer.frame[idx];
179262306a36Sopenharmony_ci	/* search done.  now find out if should be acquiring on this channel */
179362306a36Sopenharmony_ci	if (!vb2_is_streaming(&vc->vb_vidq)) {
179462306a36Sopenharmony_ci		/* we found a frame, but this channel is turned off */
179562306a36Sopenharmony_ci		frm->ulState = S2255_READ_IDLE;
179662306a36Sopenharmony_ci		return -EINVAL;
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	if (frm->ulState == S2255_READ_IDLE) {
180062306a36Sopenharmony_ci		frm->ulState = S2255_READ_FRAME;
180162306a36Sopenharmony_ci		frm->cur_size = 0;
180262306a36Sopenharmony_ci	}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	/* skip the marker 512 bytes (and offset if out of sync) */
180562306a36Sopenharmony_ci	psrc = (u8 *)pipe_info->transfer_buffer + offset;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	if (frm->lpvbits == NULL) {
180962306a36Sopenharmony_ci		dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d",
181062306a36Sopenharmony_ci			frm, dev, dev->cc, idx);
181162306a36Sopenharmony_ci		return -ENOMEM;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	pdest = frm->lpvbits + frm->cur_size;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	copy_size = (pipe_info->cur_transfer_size - offset);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	size = vc->pkt_size - PREFIX_SIZE;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* sanity check on pdest */
182162306a36Sopenharmony_ci	if ((copy_size + frm->cur_size) < vc->req_image_size)
182262306a36Sopenharmony_ci		memcpy(pdest, psrc, copy_size);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	frm->cur_size += copy_size;
182562306a36Sopenharmony_ci	dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (frm->cur_size >= size) {
182862306a36Sopenharmony_ci		dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n",
182962306a36Sopenharmony_ci			dev->cc, idx);
183062306a36Sopenharmony_ci		vc->last_frame = vc->cur_frame;
183162306a36Sopenharmony_ci		vc->cur_frame++;
183262306a36Sopenharmony_ci		/* end of system frame ring buffer, start at zero */
183362306a36Sopenharmony_ci		if ((vc->cur_frame == SYS_FRAMES) ||
183462306a36Sopenharmony_ci		    (vc->cur_frame == vc->buffer.dwFrames))
183562306a36Sopenharmony_ci			vc->cur_frame = 0;
183662306a36Sopenharmony_ci		/* frame ready */
183762306a36Sopenharmony_ci		if (vb2_is_streaming(&vc->vb_vidq))
183862306a36Sopenharmony_ci			s2255_got_frame(vc, vc->jpg_size);
183962306a36Sopenharmony_ci		vc->frame_count++;
184062306a36Sopenharmony_ci		frm->ulState = S2255_READ_IDLE;
184162306a36Sopenharmony_ci		frm->cur_size = 0;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci	/* done successfully */
184562306a36Sopenharmony_ci	return 0;
184662306a36Sopenharmony_ci}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_cistatic void s2255_read_video_callback(struct s2255_dev *dev,
184962306a36Sopenharmony_ci				      struct s2255_pipeinfo *pipe_info)
185062306a36Sopenharmony_ci{
185162306a36Sopenharmony_ci	int res;
185262306a36Sopenharmony_ci	dprintk(dev, 50, "callback read video\n");
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	if (dev->cc >= MAX_CHANNELS) {
185562306a36Sopenharmony_ci		dev->cc = 0;
185662306a36Sopenharmony_ci		dev_err(&dev->udev->dev, "invalid channel\n");
185762306a36Sopenharmony_ci		return;
185862306a36Sopenharmony_ci	}
185962306a36Sopenharmony_ci	/* otherwise copy to the system buffers */
186062306a36Sopenharmony_ci	res = save_frame(dev, pipe_info);
186162306a36Sopenharmony_ci	if (res != 0)
186262306a36Sopenharmony_ci		dprintk(dev, 4, "s2255: read callback failed\n");
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	dprintk(dev, 50, "callback read video done\n");
186562306a36Sopenharmony_ci	return;
186662306a36Sopenharmony_ci}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_cistatic long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
186962306a36Sopenharmony_ci			     u16 Index, u16 Value, void *TransferBuffer,
187062306a36Sopenharmony_ci			     s32 TransferBufferLength, int bOut)
187162306a36Sopenharmony_ci{
187262306a36Sopenharmony_ci	int r;
187362306a36Sopenharmony_ci	unsigned char *buf;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	buf = kmalloc(TransferBufferLength, GFP_KERNEL);
187662306a36Sopenharmony_ci	if (!buf)
187762306a36Sopenharmony_ci		return -ENOMEM;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	if (!bOut) {
188062306a36Sopenharmony_ci		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
188162306a36Sopenharmony_ci				    Request,
188262306a36Sopenharmony_ci				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
188362306a36Sopenharmony_ci				    USB_DIR_IN,
188462306a36Sopenharmony_ci				    Value, Index, buf,
188562306a36Sopenharmony_ci				    TransferBufferLength, USB_CTRL_SET_TIMEOUT);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci		if (r >= 0)
188862306a36Sopenharmony_ci			memcpy(TransferBuffer, buf, TransferBufferLength);
188962306a36Sopenharmony_ci	} else {
189062306a36Sopenharmony_ci		memcpy(buf, TransferBuffer, TransferBufferLength);
189162306a36Sopenharmony_ci		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
189262306a36Sopenharmony_ci				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
189362306a36Sopenharmony_ci				    Value, Index, buf,
189462306a36Sopenharmony_ci				    TransferBufferLength, USB_CTRL_SET_TIMEOUT);
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci	kfree(buf);
189762306a36Sopenharmony_ci	return r;
189862306a36Sopenharmony_ci}
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci/*
190162306a36Sopenharmony_ci * retrieve FX2 firmware version. future use.
190262306a36Sopenharmony_ci * @param dev pointer to device extension
190362306a36Sopenharmony_ci * @return -1 for fail, else returns firmware version as an int(16 bits)
190462306a36Sopenharmony_ci */
190562306a36Sopenharmony_cistatic int s2255_get_fx2fw(struct s2255_dev *dev)
190662306a36Sopenharmony_ci{
190762306a36Sopenharmony_ci	int fw;
190862306a36Sopenharmony_ci	int ret;
190962306a36Sopenharmony_ci	unsigned char transBuffer[64];
191062306a36Sopenharmony_ci	ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
191162306a36Sopenharmony_ci			       S2255_VR_IN);
191262306a36Sopenharmony_ci	if (ret < 0)
191362306a36Sopenharmony_ci		dprintk(dev, 2, "get fw error: %x\n", ret);
191462306a36Sopenharmony_ci	fw = transBuffer[0] + (transBuffer[1] << 8);
191562306a36Sopenharmony_ci	dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
191662306a36Sopenharmony_ci	return fw;
191762306a36Sopenharmony_ci}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci/*
192062306a36Sopenharmony_ci * Create the system ring buffer to copy frames into from the
192162306a36Sopenharmony_ci * usb read pipe.
192262306a36Sopenharmony_ci */
192362306a36Sopenharmony_cistatic int s2255_create_sys_buffers(struct s2255_vc *vc)
192462306a36Sopenharmony_ci{
192562306a36Sopenharmony_ci	unsigned long i;
192662306a36Sopenharmony_ci	unsigned long reqsize;
192762306a36Sopenharmony_ci	vc->buffer.dwFrames = SYS_FRAMES;
192862306a36Sopenharmony_ci	/* always allocate maximum size(PAL) for system buffers */
192962306a36Sopenharmony_ci	reqsize = SYS_FRAMES_MAXSIZE;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	if (reqsize > SYS_FRAMES_MAXSIZE)
193262306a36Sopenharmony_ci		reqsize = SYS_FRAMES_MAXSIZE;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	for (i = 0; i < SYS_FRAMES; i++) {
193562306a36Sopenharmony_ci		/* allocate the frames */
193662306a36Sopenharmony_ci		vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
193762306a36Sopenharmony_ci		vc->buffer.frame[i].size = reqsize;
193862306a36Sopenharmony_ci		if (vc->buffer.frame[i].lpvbits == NULL) {
193962306a36Sopenharmony_ci			pr_info("out of memory.  using less frames\n");
194062306a36Sopenharmony_ci			vc->buffer.dwFrames = i;
194162306a36Sopenharmony_ci			break;
194262306a36Sopenharmony_ci		}
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/* make sure internal states are set */
194662306a36Sopenharmony_ci	for (i = 0; i < SYS_FRAMES; i++) {
194762306a36Sopenharmony_ci		vc->buffer.frame[i].ulState = 0;
194862306a36Sopenharmony_ci		vc->buffer.frame[i].cur_size = 0;
194962306a36Sopenharmony_ci	}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	vc->cur_frame = 0;
195262306a36Sopenharmony_ci	vc->last_frame = -1;
195362306a36Sopenharmony_ci	return 0;
195462306a36Sopenharmony_ci}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic int s2255_release_sys_buffers(struct s2255_vc *vc)
195762306a36Sopenharmony_ci{
195862306a36Sopenharmony_ci	unsigned long i;
195962306a36Sopenharmony_ci	for (i = 0; i < SYS_FRAMES; i++) {
196062306a36Sopenharmony_ci		vfree(vc->buffer.frame[i].lpvbits);
196162306a36Sopenharmony_ci		vc->buffer.frame[i].lpvbits = NULL;
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci	return 0;
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic int s2255_board_init(struct s2255_dev *dev)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
196962306a36Sopenharmony_ci	int fw_ver;
197062306a36Sopenharmony_ci	int j;
197162306a36Sopenharmony_ci	struct s2255_pipeinfo *pipe = &dev->pipe;
197262306a36Sopenharmony_ci	dprintk(dev, 4, "board init: %p", dev);
197362306a36Sopenharmony_ci	memset(pipe, 0, sizeof(*pipe));
197462306a36Sopenharmony_ci	pipe->dev = dev;
197562306a36Sopenharmony_ci	pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
197662306a36Sopenharmony_ci	pipe->max_transfer_size = S2255_USB_XFER_SIZE;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
197962306a36Sopenharmony_ci					GFP_KERNEL);
198062306a36Sopenharmony_ci	if (pipe->transfer_buffer == NULL) {
198162306a36Sopenharmony_ci		dprintk(dev, 1, "out of memory!\n");
198262306a36Sopenharmony_ci		return -ENOMEM;
198362306a36Sopenharmony_ci	}
198462306a36Sopenharmony_ci	/* query the firmware */
198562306a36Sopenharmony_ci	fw_ver = s2255_get_fx2fw(dev);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	pr_info("s2255: usb firmware version %d.%d\n",
198862306a36Sopenharmony_ci		(fw_ver >> 8) & 0xff,
198962306a36Sopenharmony_ci		fw_ver & 0xff);
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	if (fw_ver < S2255_CUR_USB_FWVER)
199262306a36Sopenharmony_ci		pr_info("s2255: newer USB firmware available\n");
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	for (j = 0; j < MAX_CHANNELS; j++) {
199562306a36Sopenharmony_ci		struct s2255_vc *vc = &dev->vc[j];
199662306a36Sopenharmony_ci		vc->mode = mode_def;
199762306a36Sopenharmony_ci		if (dev->pid == 0x2257 && j > 1)
199862306a36Sopenharmony_ci			vc->mode.color |= (1 << 16);
199962306a36Sopenharmony_ci		vc->jpegqual = S2255_DEF_JPEG_QUAL;
200062306a36Sopenharmony_ci		vc->width = LINE_SZ_4CIFS_NTSC;
200162306a36Sopenharmony_ci		vc->height = NUM_LINES_4CIFS_NTSC * 2;
200262306a36Sopenharmony_ci		vc->std = V4L2_STD_NTSC_M;
200362306a36Sopenharmony_ci		vc->fmt = &formats[0];
200462306a36Sopenharmony_ci		vc->mode.restart = 1;
200562306a36Sopenharmony_ci		vc->req_image_size = get_transfer_size(&mode_def);
200662306a36Sopenharmony_ci		vc->frame_count = 0;
200762306a36Sopenharmony_ci		/* create the system buffers */
200862306a36Sopenharmony_ci		s2255_create_sys_buffers(vc);
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci	/* start read pipe */
201162306a36Sopenharmony_ci	s2255_start_readpipe(dev);
201262306a36Sopenharmony_ci	dprintk(dev, 1, "%s: success\n", __func__);
201362306a36Sopenharmony_ci	return 0;
201462306a36Sopenharmony_ci}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_cistatic int s2255_board_shutdown(struct s2255_dev *dev)
201762306a36Sopenharmony_ci{
201862306a36Sopenharmony_ci	u32 i;
201962306a36Sopenharmony_ci	dprintk(dev, 1, "%s: dev: %p", __func__,  dev);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
202262306a36Sopenharmony_ci		if (vb2_is_streaming(&dev->vc[i].vb_vidq))
202362306a36Sopenharmony_ci			s2255_stop_acquire(&dev->vc[i]);
202462306a36Sopenharmony_ci	}
202562306a36Sopenharmony_ci	s2255_stop_readpipe(dev);
202662306a36Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++)
202762306a36Sopenharmony_ci		s2255_release_sys_buffers(&dev->vc[i]);
202862306a36Sopenharmony_ci	/* release transfer buffer */
202962306a36Sopenharmony_ci	kfree(dev->pipe.transfer_buffer);
203062306a36Sopenharmony_ci	return 0;
203162306a36Sopenharmony_ci}
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_cistatic void read_pipe_completion(struct urb *purb)
203462306a36Sopenharmony_ci{
203562306a36Sopenharmony_ci	struct s2255_pipeinfo *pipe_info;
203662306a36Sopenharmony_ci	struct s2255_dev *dev;
203762306a36Sopenharmony_ci	int status;
203862306a36Sopenharmony_ci	int pipe;
203962306a36Sopenharmony_ci	pipe_info = purb->context;
204062306a36Sopenharmony_ci	if (pipe_info == NULL) {
204162306a36Sopenharmony_ci		dev_err(&purb->dev->dev, "no context!\n");
204262306a36Sopenharmony_ci		return;
204362306a36Sopenharmony_ci	}
204462306a36Sopenharmony_ci	dev = pipe_info->dev;
204562306a36Sopenharmony_ci	if (dev == NULL) {
204662306a36Sopenharmony_ci		dev_err(&purb->dev->dev, "no context!\n");
204762306a36Sopenharmony_ci		return;
204862306a36Sopenharmony_ci	}
204962306a36Sopenharmony_ci	status = purb->status;
205062306a36Sopenharmony_ci	/* if shutting down, do not resubmit, exit immediately */
205162306a36Sopenharmony_ci	if (status == -ESHUTDOWN) {
205262306a36Sopenharmony_ci		dprintk(dev, 2, "%s: err shutdown\n", __func__);
205362306a36Sopenharmony_ci		pipe_info->err_count++;
205462306a36Sopenharmony_ci		return;
205562306a36Sopenharmony_ci	}
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	if (pipe_info->state == 0) {
205862306a36Sopenharmony_ci		dprintk(dev, 2, "%s: exiting USB pipe", __func__);
205962306a36Sopenharmony_ci		return;
206062306a36Sopenharmony_ci	}
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	if (status == 0)
206362306a36Sopenharmony_ci		s2255_read_video_callback(dev, pipe_info);
206462306a36Sopenharmony_ci	else {
206562306a36Sopenharmony_ci		pipe_info->err_count++;
206662306a36Sopenharmony_ci		dprintk(dev, 1, "%s: failed URB %d\n", __func__, status);
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
207062306a36Sopenharmony_ci	/* reuse urb */
207162306a36Sopenharmony_ci	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
207262306a36Sopenharmony_ci			  pipe,
207362306a36Sopenharmony_ci			  pipe_info->transfer_buffer,
207462306a36Sopenharmony_ci			  pipe_info->cur_transfer_size,
207562306a36Sopenharmony_ci			  read_pipe_completion, pipe_info);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	if (pipe_info->state != 0) {
207862306a36Sopenharmony_ci		if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC))
207962306a36Sopenharmony_ci			dev_err(&dev->udev->dev, "error submitting urb\n");
208062306a36Sopenharmony_ci	} else {
208162306a36Sopenharmony_ci		dprintk(dev, 2, "%s :complete state 0\n", __func__);
208262306a36Sopenharmony_ci	}
208362306a36Sopenharmony_ci	return;
208462306a36Sopenharmony_ci}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_cistatic int s2255_start_readpipe(struct s2255_dev *dev)
208762306a36Sopenharmony_ci{
208862306a36Sopenharmony_ci	int pipe;
208962306a36Sopenharmony_ci	int retval;
209062306a36Sopenharmony_ci	struct s2255_pipeinfo *pipe_info = &dev->pipe;
209162306a36Sopenharmony_ci	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
209262306a36Sopenharmony_ci	dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint);
209362306a36Sopenharmony_ci	pipe_info->state = 1;
209462306a36Sopenharmony_ci	pipe_info->err_count = 0;
209562306a36Sopenharmony_ci	pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
209662306a36Sopenharmony_ci	if (!pipe_info->stream_urb)
209762306a36Sopenharmony_ci		return -ENOMEM;
209862306a36Sopenharmony_ci	/* transfer buffer allocated in board_init */
209962306a36Sopenharmony_ci	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
210062306a36Sopenharmony_ci			  pipe,
210162306a36Sopenharmony_ci			  pipe_info->transfer_buffer,
210262306a36Sopenharmony_ci			  pipe_info->cur_transfer_size,
210362306a36Sopenharmony_ci			  read_pipe_completion, pipe_info);
210462306a36Sopenharmony_ci	retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
210562306a36Sopenharmony_ci	if (retval) {
210662306a36Sopenharmony_ci		pr_err("s2255: start read pipe failed\n");
210762306a36Sopenharmony_ci		return retval;
210862306a36Sopenharmony_ci	}
210962306a36Sopenharmony_ci	return 0;
211062306a36Sopenharmony_ci}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci/* starts acquisition process */
211362306a36Sopenharmony_cistatic int s2255_start_acquire(struct s2255_vc *vc)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	int res;
211662306a36Sopenharmony_ci	unsigned long chn_rev;
211762306a36Sopenharmony_ci	int j;
211862306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
211962306a36Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	mutex_lock(&dev->cmdlock);
212262306a36Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
212362306a36Sopenharmony_ci	vc->last_frame = -1;
212462306a36Sopenharmony_ci	vc->bad_payload = 0;
212562306a36Sopenharmony_ci	vc->cur_frame = 0;
212662306a36Sopenharmony_ci	for (j = 0; j < SYS_FRAMES; j++) {
212762306a36Sopenharmony_ci		vc->buffer.frame[j].ulState = 0;
212862306a36Sopenharmony_ci		vc->buffer.frame[j].cur_size = 0;
212962306a36Sopenharmony_ci	}
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	/* send the start command */
213262306a36Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
213362306a36Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
213462306a36Sopenharmony_ci	buffer[2] = CMD_START;
213562306a36Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
213662306a36Sopenharmony_ci	if (res != 0)
213762306a36Sopenharmony_ci		dev_err(&dev->udev->dev, "CMD_START error\n");
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res);
214062306a36Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
214162306a36Sopenharmony_ci	return res;
214262306a36Sopenharmony_ci}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cistatic int s2255_stop_acquire(struct s2255_vc *vc)
214562306a36Sopenharmony_ci{
214662306a36Sopenharmony_ci	int res;
214762306a36Sopenharmony_ci	unsigned long chn_rev;
214862306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
214962306a36Sopenharmony_ci	__le32 *buffer = dev->cmdbuf;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	mutex_lock(&dev->cmdlock);
215262306a36Sopenharmony_ci	chn_rev = G_chnmap[vc->idx];
215362306a36Sopenharmony_ci	/* send the stop command */
215462306a36Sopenharmony_ci	buffer[0] = IN_DATA_TOKEN;
215562306a36Sopenharmony_ci	buffer[1] = (__le32) cpu_to_le32(chn_rev);
215662306a36Sopenharmony_ci	buffer[2] = CMD_STOP;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
215962306a36Sopenharmony_ci	if (res != 0)
216062306a36Sopenharmony_ci		dev_err(&dev->udev->dev, "CMD_STOP error\n");
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res);
216362306a36Sopenharmony_ci	mutex_unlock(&dev->cmdlock);
216462306a36Sopenharmony_ci	return res;
216562306a36Sopenharmony_ci}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_cistatic void s2255_stop_readpipe(struct s2255_dev *dev)
216862306a36Sopenharmony_ci{
216962306a36Sopenharmony_ci	struct s2255_pipeinfo *pipe = &dev->pipe;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	pipe->state = 0;
217262306a36Sopenharmony_ci	if (pipe->stream_urb) {
217362306a36Sopenharmony_ci		/* cancel urb */
217462306a36Sopenharmony_ci		usb_kill_urb(pipe->stream_urb);
217562306a36Sopenharmony_ci		usb_free_urb(pipe->stream_urb);
217662306a36Sopenharmony_ci		pipe->stream_urb = NULL;
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci	dprintk(dev, 4, "%s", __func__);
217962306a36Sopenharmony_ci	return;
218062306a36Sopenharmony_ci}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_cistatic void s2255_fwload_start(struct s2255_dev *dev)
218362306a36Sopenharmony_ci{
218462306a36Sopenharmony_ci	s2255_reset_dsppower(dev);
218562306a36Sopenharmony_ci	dev->fw_data->fw_size = dev->fw_data->fw->size;
218662306a36Sopenharmony_ci	atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
218762306a36Sopenharmony_ci	memcpy(dev->fw_data->pfw_data,
218862306a36Sopenharmony_ci	       dev->fw_data->fw->data, CHUNK_SIZE);
218962306a36Sopenharmony_ci	dev->fw_data->fw_loaded = CHUNK_SIZE;
219062306a36Sopenharmony_ci	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
219162306a36Sopenharmony_ci			  usb_sndbulkpipe(dev->udev, 2),
219262306a36Sopenharmony_ci			  dev->fw_data->pfw_data,
219362306a36Sopenharmony_ci			  CHUNK_SIZE, s2255_fwchunk_complete,
219462306a36Sopenharmony_ci			  dev->fw_data);
219562306a36Sopenharmony_ci	mod_timer(&dev->timer, jiffies + HZ);
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci/* standard usb probe function */
219962306a36Sopenharmony_cistatic int s2255_probe(struct usb_interface *interface,
220062306a36Sopenharmony_ci		       const struct usb_device_id *id)
220162306a36Sopenharmony_ci{
220262306a36Sopenharmony_ci	struct s2255_dev *dev = NULL;
220362306a36Sopenharmony_ci	struct usb_host_interface *iface_desc;
220462306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
220562306a36Sopenharmony_ci	int i;
220662306a36Sopenharmony_ci	int retval = -ENOMEM;
220762306a36Sopenharmony_ci	__le32 *pdata;
220862306a36Sopenharmony_ci	int fw_size;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	/* allocate memory for our device state and initialize it to zero */
221162306a36Sopenharmony_ci	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
221262306a36Sopenharmony_ci	if (dev == NULL) {
221362306a36Sopenharmony_ci		s2255_dev_err(&interface->dev, "out of memory\n");
221462306a36Sopenharmony_ci		return -ENOMEM;
221562306a36Sopenharmony_ci	}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
221862306a36Sopenharmony_ci	if (dev->cmdbuf == NULL) {
221962306a36Sopenharmony_ci		s2255_dev_err(&interface->dev, "out of memory\n");
222062306a36Sopenharmony_ci		goto errorFWDATA1;
222162306a36Sopenharmony_ci	}
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	atomic_set(&dev->num_channels, 0);
222462306a36Sopenharmony_ci	dev->pid = id->idProduct;
222562306a36Sopenharmony_ci	dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
222662306a36Sopenharmony_ci	if (!dev->fw_data)
222762306a36Sopenharmony_ci		goto errorFWDATA1;
222862306a36Sopenharmony_ci	mutex_init(&dev->lock);
222962306a36Sopenharmony_ci	mutex_init(&dev->cmdlock);
223062306a36Sopenharmony_ci	/* grab usb_device and save it */
223162306a36Sopenharmony_ci	dev->udev = usb_get_dev(interface_to_usbdev(interface));
223262306a36Sopenharmony_ci	if (dev->udev == NULL) {
223362306a36Sopenharmony_ci		dev_err(&interface->dev, "null usb device\n");
223462306a36Sopenharmony_ci		retval = -ENODEV;
223562306a36Sopenharmony_ci		goto errorUDEV;
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci	dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
223862306a36Sopenharmony_ci		dev, dev->udev, interface);
223962306a36Sopenharmony_ci	dev->interface = interface;
224062306a36Sopenharmony_ci	/* set up the endpoint information  */
224162306a36Sopenharmony_ci	iface_desc = interface->cur_altsetting;
224262306a36Sopenharmony_ci	dev_dbg(&interface->dev, "num EP: %d\n",
224362306a36Sopenharmony_ci		iface_desc->desc.bNumEndpoints);
224462306a36Sopenharmony_ci	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
224562306a36Sopenharmony_ci		endpoint = &iface_desc->endpoint[i].desc;
224662306a36Sopenharmony_ci		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
224762306a36Sopenharmony_ci			/* we found the bulk in endpoint */
224862306a36Sopenharmony_ci			dev->read_endpoint = endpoint->bEndpointAddress;
224962306a36Sopenharmony_ci		}
225062306a36Sopenharmony_ci	}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	if (!dev->read_endpoint) {
225362306a36Sopenharmony_ci		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
225462306a36Sopenharmony_ci		goto errorEP;
225562306a36Sopenharmony_ci	}
225662306a36Sopenharmony_ci	timer_setup(&dev->timer, s2255_timer, 0);
225762306a36Sopenharmony_ci	init_waitqueue_head(&dev->fw_data->wait_fw);
225862306a36Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
225962306a36Sopenharmony_ci		struct s2255_vc *vc = &dev->vc[i];
226062306a36Sopenharmony_ci		vc->idx = i;
226162306a36Sopenharmony_ci		vc->dev = dev;
226262306a36Sopenharmony_ci		init_waitqueue_head(&vc->wait_setmode);
226362306a36Sopenharmony_ci		init_waitqueue_head(&vc->wait_vidstatus);
226462306a36Sopenharmony_ci		spin_lock_init(&vc->qlock);
226562306a36Sopenharmony_ci		mutex_init(&vc->vb_lock);
226662306a36Sopenharmony_ci	}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
226962306a36Sopenharmony_ci	if (!dev->fw_data->fw_urb)
227062306a36Sopenharmony_ci		goto errorFWURB;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
227362306a36Sopenharmony_ci	if (!dev->fw_data->pfw_data) {
227462306a36Sopenharmony_ci		dev_err(&interface->dev, "out of memory!\n");
227562306a36Sopenharmony_ci		goto errorFWDATA2;
227662306a36Sopenharmony_ci	}
227762306a36Sopenharmony_ci	/* load the first chunk */
227862306a36Sopenharmony_ci	if (request_firmware(&dev->fw_data->fw,
227962306a36Sopenharmony_ci			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
228062306a36Sopenharmony_ci		dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n");
228162306a36Sopenharmony_ci		goto errorREQFW;
228262306a36Sopenharmony_ci	}
228362306a36Sopenharmony_ci	/* check the firmware is valid */
228462306a36Sopenharmony_ci	fw_size = dev->fw_data->fw->size;
228562306a36Sopenharmony_ci	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	if (*pdata != S2255_FW_MARKER) {
228862306a36Sopenharmony_ci		dev_err(&interface->dev, "Firmware invalid.\n");
228962306a36Sopenharmony_ci		retval = -ENODEV;
229062306a36Sopenharmony_ci		goto errorFWMARKER;
229162306a36Sopenharmony_ci	} else {
229262306a36Sopenharmony_ci		/* make sure firmware is the latest */
229362306a36Sopenharmony_ci		__le32 *pRel;
229462306a36Sopenharmony_ci		pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
229562306a36Sopenharmony_ci		pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
229662306a36Sopenharmony_ci		dev->dsp_fw_ver = le32_to_cpu(*pRel);
229762306a36Sopenharmony_ci		if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
229862306a36Sopenharmony_ci			pr_info("s2255: f2255usb.bin out of date.\n");
229962306a36Sopenharmony_ci		if (dev->pid == 0x2257 &&
230062306a36Sopenharmony_ci				dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
230162306a36Sopenharmony_ci			pr_warn("2257 needs firmware %d or above.\n",
230262306a36Sopenharmony_ci				S2255_MIN_DSP_COLORFILTER);
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci	usb_reset_device(dev->udev);
230562306a36Sopenharmony_ci	/* load 2255 board specific */
230662306a36Sopenharmony_ci	retval = s2255_board_init(dev);
230762306a36Sopenharmony_ci	if (retval)
230862306a36Sopenharmony_ci		goto errorBOARDINIT;
230962306a36Sopenharmony_ci	s2255_fwload_start(dev);
231062306a36Sopenharmony_ci	/* loads v4l specific */
231162306a36Sopenharmony_ci	retval = s2255_probe_v4l(dev);
231262306a36Sopenharmony_ci	if (retval)
231362306a36Sopenharmony_ci		goto errorBOARDINIT;
231462306a36Sopenharmony_ci	dev_info(&interface->dev, "Sensoray 2255 detected\n");
231562306a36Sopenharmony_ci	return 0;
231662306a36Sopenharmony_cierrorBOARDINIT:
231762306a36Sopenharmony_ci	s2255_board_shutdown(dev);
231862306a36Sopenharmony_cierrorFWMARKER:
231962306a36Sopenharmony_ci	release_firmware(dev->fw_data->fw);
232062306a36Sopenharmony_cierrorREQFW:
232162306a36Sopenharmony_ci	kfree(dev->fw_data->pfw_data);
232262306a36Sopenharmony_cierrorFWDATA2:
232362306a36Sopenharmony_ci	usb_free_urb(dev->fw_data->fw_urb);
232462306a36Sopenharmony_cierrorFWURB:
232562306a36Sopenharmony_ci	timer_shutdown_sync(&dev->timer);
232662306a36Sopenharmony_cierrorEP:
232762306a36Sopenharmony_ci	usb_put_dev(dev->udev);
232862306a36Sopenharmony_cierrorUDEV:
232962306a36Sopenharmony_ci	kfree(dev->fw_data);
233062306a36Sopenharmony_ci	mutex_destroy(&dev->lock);
233162306a36Sopenharmony_cierrorFWDATA1:
233262306a36Sopenharmony_ci	kfree(dev->cmdbuf);
233362306a36Sopenharmony_ci	kfree(dev);
233462306a36Sopenharmony_ci	pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval);
233562306a36Sopenharmony_ci	return retval;
233662306a36Sopenharmony_ci}
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci/* disconnect routine. when board is removed physically or with rmmod */
233962306a36Sopenharmony_cistatic void s2255_disconnect(struct usb_interface *interface)
234062306a36Sopenharmony_ci{
234162306a36Sopenharmony_ci	struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
234262306a36Sopenharmony_ci	int i;
234362306a36Sopenharmony_ci	int channels = atomic_read(&dev->num_channels);
234462306a36Sopenharmony_ci	mutex_lock(&dev->lock);
234562306a36Sopenharmony_ci	v4l2_device_disconnect(&dev->v4l2_dev);
234662306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
234762306a36Sopenharmony_ci	/*see comments in the uvc_driver.c usb disconnect function */
234862306a36Sopenharmony_ci	atomic_inc(&dev->num_channels);
234962306a36Sopenharmony_ci	/* unregister each video device. */
235062306a36Sopenharmony_ci	for (i = 0; i < channels; i++)
235162306a36Sopenharmony_ci		video_unregister_device(&dev->vc[i].vdev);
235262306a36Sopenharmony_ci	/* wake up any of our timers */
235362306a36Sopenharmony_ci	atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
235462306a36Sopenharmony_ci	wake_up(&dev->fw_data->wait_fw);
235562306a36Sopenharmony_ci	for (i = 0; i < MAX_CHANNELS; i++) {
235662306a36Sopenharmony_ci		dev->vc[i].setmode_ready = 1;
235762306a36Sopenharmony_ci		wake_up(&dev->vc[i].wait_setmode);
235862306a36Sopenharmony_ci		dev->vc[i].vidstatus_ready = 1;
235962306a36Sopenharmony_ci		wake_up(&dev->vc[i].wait_vidstatus);
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci	if (atomic_dec_and_test(&dev->num_channels))
236262306a36Sopenharmony_ci		s2255_destroy(dev);
236362306a36Sopenharmony_ci	dev_info(&interface->dev, "%s\n", __func__);
236462306a36Sopenharmony_ci}
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_cistatic struct usb_driver s2255_driver = {
236762306a36Sopenharmony_ci	.name = S2255_DRIVER_NAME,
236862306a36Sopenharmony_ci	.probe = s2255_probe,
236962306a36Sopenharmony_ci	.disconnect = s2255_disconnect,
237062306a36Sopenharmony_ci	.id_table = s2255_table,
237162306a36Sopenharmony_ci};
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_cimodule_usb_driver(s2255_driver);
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ciMODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
237662306a36Sopenharmony_ciMODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
237762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
237862306a36Sopenharmony_ciMODULE_VERSION(S2255_VERSION);
237962306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_FILE_NAME);
2380