1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20aa77f6cSMauro Carvalho Chehab /* 30aa77f6cSMauro Carvalho Chehab * s2255drv.c - a driver for the Sensoray 2255 USB video capture device 40aa77f6cSMauro Carvalho Chehab * 5d86c6a8cSDean Anderson * Copyright (C) 2007-2014 by Sensoray Company Inc. 60aa77f6cSMauro Carvalho Chehab * Dean Anderson 70aa77f6cSMauro Carvalho Chehab * 80aa77f6cSMauro Carvalho Chehab * Some video buffer code based on vivi driver: 90aa77f6cSMauro Carvalho Chehab * 100aa77f6cSMauro Carvalho Chehab * Sensoray 2255 device supports 4 simultaneous channels. 110aa77f6cSMauro Carvalho Chehab * The channels are not "crossbar" inputs, they are physically 120aa77f6cSMauro Carvalho Chehab * attached to separate video decoders. 130aa77f6cSMauro Carvalho Chehab * 140aa77f6cSMauro Carvalho Chehab * Because of USB2.0 bandwidth limitations. There is only a 150aa77f6cSMauro Carvalho Chehab * certain amount of data which may be transferred at one time. 160aa77f6cSMauro Carvalho Chehab * 170aa77f6cSMauro Carvalho Chehab * Example maximum bandwidth utilization: 180aa77f6cSMauro Carvalho Chehab * 190aa77f6cSMauro Carvalho Chehab * -full size, color mode YUYV or YUV422P: 2 channels at once 200aa77f6cSMauro Carvalho Chehab * -full or half size Grey scale: all 4 channels at once 210aa77f6cSMauro Carvalho Chehab * -half size, color mode YUYV or YUV422P: all 4 channels at once 220aa77f6cSMauro Carvalho Chehab * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels 230aa77f6cSMauro Carvalho Chehab * at once. 240aa77f6cSMauro Carvalho Chehab */ 250aa77f6cSMauro Carvalho Chehab 260aa77f6cSMauro Carvalho Chehab #include <linux/module.h> 270aa77f6cSMauro Carvalho Chehab #include <linux/firmware.h> 280aa77f6cSMauro Carvalho Chehab #include <linux/kernel.h> 290aa77f6cSMauro Carvalho Chehab #include <linux/mutex.h> 300aa77f6cSMauro Carvalho Chehab #include <linux/slab.h> 310aa77f6cSMauro Carvalho Chehab #include <linux/videodev2.h> 320aa77f6cSMauro Carvalho Chehab #include <linux/mm.h> 3344d06d82SHans Verkuil #include <linux/vmalloc.h> 3444d06d82SHans Verkuil #include <linux/usb.h> 352d700715SJunghak Sung #include <media/videobuf2-v4l2.h> 36340a30c5Ssensoray-dev #include <media/videobuf2-vmalloc.h> 370aa77f6cSMauro Carvalho Chehab #include <media/v4l2-common.h> 380aa77f6cSMauro Carvalho Chehab #include <media/v4l2-device.h> 390aa77f6cSMauro Carvalho Chehab #include <media/v4l2-ioctl.h> 40192f1e78SHans Verkuil #include <media/v4l2-ctrls.h> 4144d06d82SHans Verkuil #include <media/v4l2-event.h> 420aa77f6cSMauro Carvalho Chehab 43340a30c5Ssensoray-dev #define S2255_VERSION "1.25.1" 440aa77f6cSMauro Carvalho Chehab #define FIRMWARE_FILE_NAME "f2255usb.bin" 450aa77f6cSMauro Carvalho Chehab 460aa77f6cSMauro Carvalho Chehab /* default JPEG quality */ 470aa77f6cSMauro Carvalho Chehab #define S2255_DEF_JPEG_QUAL 50 480aa77f6cSMauro Carvalho Chehab /* vendor request in */ 490aa77f6cSMauro Carvalho Chehab #define S2255_VR_IN 0 500aa77f6cSMauro Carvalho Chehab /* vendor request out */ 510aa77f6cSMauro Carvalho Chehab #define S2255_VR_OUT 1 520aa77f6cSMauro Carvalho Chehab /* firmware query */ 530aa77f6cSMauro Carvalho Chehab #define S2255_VR_FW 0x30 540aa77f6cSMauro Carvalho Chehab /* USB endpoint number for configuring the device */ 550aa77f6cSMauro Carvalho Chehab #define S2255_CONFIG_EP 2 560aa77f6cSMauro Carvalho Chehab /* maximum time for DSP to start responding after last FW word loaded(ms) */ 570aa77f6cSMauro Carvalho Chehab #define S2255_DSP_BOOTTIME 800 580aa77f6cSMauro Carvalho Chehab /* maximum time to wait for firmware to load (ms) */ 590aa77f6cSMauro Carvalho Chehab #define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) 609da62eb0SDean Anderson #define S2255_MIN_BUFS 2 610aa77f6cSMauro Carvalho Chehab #define S2255_SETMODE_TIMEOUT 500 620aa77f6cSMauro Carvalho Chehab #define S2255_VIDSTATUS_TIMEOUT 350 630aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) 640aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) 650aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) 660aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_FW cpu_to_le32(0x10) 670aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_STATUS cpu_to_le32(0x20) 680aa77f6cSMauro Carvalho Chehab #define S2255_USB_XFER_SIZE (16 * 1024) 690aa77f6cSMauro Carvalho Chehab #define MAX_CHANNELS 4 700aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES 4 710aa77f6cSMauro Carvalho Chehab /* maximum size is PAL full size plus room for the marker header(s) */ 720aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) 730aa77f6cSMauro Carvalho Chehab #define DEF_USB_BLOCK S2255_USB_XFER_SIZE 740aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_NTSC 640 750aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_NTSC 640 760aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_NTSC 320 770aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_PAL 704 780aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_PAL 704 790aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_PAL 352 800aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_NTSC 240 810aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_NTSC 240 820aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_NTSC 240 830aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_PAL 288 840aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_PAL 288 850aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_PAL 288 860aa77f6cSMauro Carvalho Chehab #define LINE_SZ_DEF 640 870aa77f6cSMauro Carvalho Chehab #define NUM_LINES_DEF 240 880aa77f6cSMauro Carvalho Chehab 890aa77f6cSMauro Carvalho Chehab 900aa77f6cSMauro Carvalho Chehab /* predefined settings */ 910aa77f6cSMauro Carvalho Chehab #define FORMAT_NTSC 1 920aa77f6cSMauro Carvalho Chehab #define FORMAT_PAL 2 930aa77f6cSMauro Carvalho Chehab 940aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ 950aa77f6cSMauro Carvalho Chehab #define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ 960aa77f6cSMauro Carvalho Chehab #define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ 970aa77f6cSMauro Carvalho Chehab /* SCALE_4CIFSI is the 2 fields interpolated into one */ 980aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ 990aa77f6cSMauro Carvalho Chehab 1000aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPL 1 /* YUV planar */ 1010aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPK 2 /* YUV packed */ 1020aa77f6cSMauro Carvalho Chehab #define COLOR_Y8 4 /* monochrome */ 1030aa77f6cSMauro Carvalho Chehab #define COLOR_JPG 5 /* JPEG */ 1040aa77f6cSMauro Carvalho Chehab 1050aa77f6cSMauro Carvalho Chehab #define MASK_COLOR 0x000000ff 1060aa77f6cSMauro Carvalho Chehab #define MASK_JPG_QUALITY 0x0000ff00 1070aa77f6cSMauro Carvalho Chehab #define MASK_INPUT_TYPE 0x000f0000 1080aa77f6cSMauro Carvalho Chehab /* frame decimation. */ 1090aa77f6cSMauro Carvalho Chehab #define FDEC_1 1 /* capture every frame. default */ 1100aa77f6cSMauro Carvalho Chehab #define FDEC_2 2 /* capture every 2nd frame */ 1110aa77f6cSMauro Carvalho Chehab #define FDEC_3 3 /* capture every 3rd frame */ 1120aa77f6cSMauro Carvalho Chehab #define FDEC_5 5 /* capture every 5th frame */ 1130aa77f6cSMauro Carvalho Chehab 1140aa77f6cSMauro Carvalho Chehab /*------------------------------------------------------- 1150aa77f6cSMauro Carvalho Chehab * Default mode parameters. 1160aa77f6cSMauro Carvalho Chehab *-------------------------------------------------------*/ 1170aa77f6cSMauro Carvalho Chehab #define DEF_SCALE SCALE_4CIFS 1180aa77f6cSMauro Carvalho Chehab #define DEF_COLOR COLOR_YUVPL 1190aa77f6cSMauro Carvalho Chehab #define DEF_FDEC FDEC_1 1200aa77f6cSMauro Carvalho Chehab #define DEF_BRIGHT 0 1210aa77f6cSMauro Carvalho Chehab #define DEF_CONTRAST 0x5c 1220aa77f6cSMauro Carvalho Chehab #define DEF_SATURATION 0x80 1230aa77f6cSMauro Carvalho Chehab #define DEF_HUE 0 1240aa77f6cSMauro Carvalho Chehab 1250aa77f6cSMauro Carvalho Chehab /* usb config commands */ 1260aa77f6cSMauro Carvalho Chehab #define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) 1270aa77f6cSMauro Carvalho Chehab #define CMD_2255 0xc2255000 1280aa77f6cSMauro Carvalho Chehab #define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) 1290aa77f6cSMauro Carvalho Chehab #define CMD_START cpu_to_le32((CMD_2255 | 0x20)) 1300aa77f6cSMauro Carvalho Chehab #define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) 1310aa77f6cSMauro Carvalho Chehab #define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) 1320aa77f6cSMauro Carvalho Chehab 1330aa77f6cSMauro Carvalho Chehab struct s2255_mode { 1340aa77f6cSMauro Carvalho Chehab u32 format; /* input video format (NTSC, PAL) */ 1350aa77f6cSMauro Carvalho Chehab u32 scale; /* output video scale */ 1360aa77f6cSMauro Carvalho Chehab u32 color; /* output video color format */ 1370aa77f6cSMauro Carvalho Chehab u32 fdec; /* frame decimation */ 1380aa77f6cSMauro Carvalho Chehab u32 bright; /* brightness */ 1390aa77f6cSMauro Carvalho Chehab u32 contrast; /* contrast */ 1400aa77f6cSMauro Carvalho Chehab u32 saturation; /* saturation */ 1410aa77f6cSMauro Carvalho Chehab u32 hue; /* hue (NTSC only)*/ 1420aa77f6cSMauro Carvalho Chehab u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ 1430aa77f6cSMauro Carvalho Chehab u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ 1440aa77f6cSMauro Carvalho Chehab u32 restart; /* if DSP requires restart */ 1450aa77f6cSMauro Carvalho Chehab }; 1460aa77f6cSMauro Carvalho Chehab 1470aa77f6cSMauro Carvalho Chehab 1480aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE 0 1490aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME 1 1500aa77f6cSMauro Carvalho Chehab 1510aa77f6cSMauro Carvalho Chehab /* frame structure */ 1520aa77f6cSMauro Carvalho Chehab struct s2255_framei { 1530aa77f6cSMauro Carvalho Chehab unsigned long size; 1540aa77f6cSMauro Carvalho Chehab unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ 1550aa77f6cSMauro Carvalho Chehab void *lpvbits; /* image data */ 1560aa77f6cSMauro Carvalho Chehab unsigned long cur_size; /* current data copied to it */ 1570aa77f6cSMauro Carvalho Chehab }; 1580aa77f6cSMauro Carvalho Chehab 1590aa77f6cSMauro Carvalho Chehab /* image buffer structure */ 1600aa77f6cSMauro Carvalho Chehab struct s2255_bufferi { 1610aa77f6cSMauro Carvalho Chehab unsigned long dwFrames; /* number of frames in buffer */ 1620aa77f6cSMauro Carvalho Chehab struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ 1630aa77f6cSMauro Carvalho Chehab }; 1640aa77f6cSMauro Carvalho Chehab 1650aa77f6cSMauro Carvalho Chehab #define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ 1660aa77f6cSMauro Carvalho Chehab DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ 1670aa77f6cSMauro Carvalho Chehab DEF_HUE, 0, DEF_USB_BLOCK, 0} 1680aa77f6cSMauro Carvalho Chehab 1690aa77f6cSMauro Carvalho Chehab /* for firmware loading, fw_state */ 1700aa77f6cSMauro Carvalho Chehab #define S2255_FW_NOTLOADED 0 1710aa77f6cSMauro Carvalho Chehab #define S2255_FW_LOADED_DSPWAIT 1 1720aa77f6cSMauro Carvalho Chehab #define S2255_FW_SUCCESS 2 1730aa77f6cSMauro Carvalho Chehab #define S2255_FW_FAILED 3 1740aa77f6cSMauro Carvalho Chehab #define S2255_FW_DISCONNECTING 4 1750aa77f6cSMauro Carvalho Chehab #define S2255_FW_MARKER cpu_to_le32(0x22552f2f) 1760aa77f6cSMauro Carvalho Chehab /* 2255 read states */ 1770aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE 0 1780aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME 1 1790aa77f6cSMauro Carvalho Chehab struct s2255_fw { 1800aa77f6cSMauro Carvalho Chehab int fw_loaded; 1810aa77f6cSMauro Carvalho Chehab int fw_size; 1820aa77f6cSMauro Carvalho Chehab struct urb *fw_urb; 1830aa77f6cSMauro Carvalho Chehab atomic_t fw_state; 1840aa77f6cSMauro Carvalho Chehab void *pfw_data; 1850aa77f6cSMauro Carvalho Chehab wait_queue_head_t wait_fw; 1860aa77f6cSMauro Carvalho Chehab const struct firmware *fw; 1870aa77f6cSMauro Carvalho Chehab }; 1880aa77f6cSMauro Carvalho Chehab 1890aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo { 1900aa77f6cSMauro Carvalho Chehab u32 max_transfer_size; 1910aa77f6cSMauro Carvalho Chehab u32 cur_transfer_size; 1920aa77f6cSMauro Carvalho Chehab u8 *transfer_buffer; 1930aa77f6cSMauro Carvalho Chehab u32 state; 1940aa77f6cSMauro Carvalho Chehab void *stream_urb; 1950aa77f6cSMauro Carvalho Chehab void *dev; /* back pointer to s2255_dev struct*/ 1960aa77f6cSMauro Carvalho Chehab u32 err_count; 1970aa77f6cSMauro Carvalho Chehab u32 idx; 1980aa77f6cSMauro Carvalho Chehab }; 1990aa77f6cSMauro Carvalho Chehab 2000aa77f6cSMauro Carvalho Chehab struct s2255_fmt; /*forward declaration */ 2010aa77f6cSMauro Carvalho Chehab struct s2255_dev; 2020aa77f6cSMauro Carvalho Chehab 2035e950fafSDean Anderson /* 2255 video channel */ 2045e950fafSDean Anderson struct s2255_vc { 205f5402007Ssensoray-dev struct s2255_dev *dev; 2060aa77f6cSMauro Carvalho Chehab struct video_device vdev; 207192f1e78SHans Verkuil struct v4l2_ctrl_handler hdl; 2087041dec7SHans Verkuil struct v4l2_ctrl *jpegqual_ctrl; 2090aa77f6cSMauro Carvalho Chehab int resources; 210d86c6a8cSDean Anderson struct list_head buf_list; 2110aa77f6cSMauro Carvalho Chehab struct s2255_bufferi buffer; 2120aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 213469af77aSHans Verkuil v4l2_std_id std; 2140aa77f6cSMauro Carvalho Chehab /* jpeg compression */ 2157041dec7SHans Verkuil unsigned jpegqual; 2160aa77f6cSMauro Carvalho Chehab /* capture parameters (for high quality mode full size) */ 2170aa77f6cSMauro Carvalho Chehab struct v4l2_captureparm cap_parm; 2180aa77f6cSMauro Carvalho Chehab int cur_frame; 2190aa77f6cSMauro Carvalho Chehab int last_frame; 2200aa77f6cSMauro Carvalho Chehab /* allocated image size */ 2210aa77f6cSMauro Carvalho Chehab unsigned long req_image_size; 2220aa77f6cSMauro Carvalho Chehab /* received packet size */ 2230aa77f6cSMauro Carvalho Chehab unsigned long pkt_size; 2240aa77f6cSMauro Carvalho Chehab int bad_payload; 2250aa77f6cSMauro Carvalho Chehab unsigned long frame_count; 2260aa77f6cSMauro Carvalho Chehab /* if JPEG image */ 2270aa77f6cSMauro Carvalho Chehab int jpg_size; 2280aa77f6cSMauro Carvalho Chehab /* if channel configured to default state */ 2290aa77f6cSMauro Carvalho Chehab int configured; 2300aa77f6cSMauro Carvalho Chehab wait_queue_head_t wait_setmode; 2310aa77f6cSMauro Carvalho Chehab int setmode_ready; 2320aa77f6cSMauro Carvalho Chehab /* video status items */ 2330aa77f6cSMauro Carvalho Chehab int vidstatus; 2340aa77f6cSMauro Carvalho Chehab wait_queue_head_t wait_vidstatus; 2350aa77f6cSMauro Carvalho Chehab int vidstatus_ready; 2360aa77f6cSMauro Carvalho Chehab unsigned int width; 2370aa77f6cSMauro Carvalho Chehab unsigned int height; 238340a30c5Ssensoray-dev enum v4l2_field field; 2390aa77f6cSMauro Carvalho Chehab const struct s2255_fmt *fmt; 2400aa77f6cSMauro Carvalho Chehab int idx; /* channel number on device, 0-3 */ 241340a30c5Ssensoray-dev struct vb2_queue vb_vidq; 242340a30c5Ssensoray-dev struct mutex vb_lock; /* streaming lock */ 243340a30c5Ssensoray-dev spinlock_t qlock; 2440aa77f6cSMauro Carvalho Chehab }; 2450aa77f6cSMauro Carvalho Chehab 2460aa77f6cSMauro Carvalho Chehab 2470aa77f6cSMauro Carvalho Chehab struct s2255_dev { 2485e950fafSDean Anderson struct s2255_vc vc[MAX_CHANNELS]; 2490aa77f6cSMauro Carvalho Chehab struct v4l2_device v4l2_dev; 2500aa77f6cSMauro Carvalho Chehab atomic_t num_channels; 2510aa77f6cSMauro Carvalho Chehab int frames; 2520aa77f6cSMauro Carvalho Chehab struct mutex lock; /* channels[].vdev.lock */ 25347d8c881SDean Anderson struct mutex cmdlock; /* protects cmdbuf */ 2540aa77f6cSMauro Carvalho Chehab struct usb_device *udev; 2550aa77f6cSMauro Carvalho Chehab struct usb_interface *interface; 2560aa77f6cSMauro Carvalho Chehab u8 read_endpoint; 2570aa77f6cSMauro Carvalho Chehab struct timer_list timer; 2580aa77f6cSMauro Carvalho Chehab struct s2255_fw *fw_data; 2590aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo pipe; 2600aa77f6cSMauro Carvalho Chehab u32 cc; /* current channel */ 2610aa77f6cSMauro Carvalho Chehab int frame_ready; 2620aa77f6cSMauro Carvalho Chehab int chn_ready; 2630aa77f6cSMauro Carvalho Chehab /* dsp firmware version (f2255usb.bin) */ 2640aa77f6cSMauro Carvalho Chehab int dsp_fw_ver; 2650aa77f6cSMauro Carvalho Chehab u16 pid; /* product id */ 26647d8c881SDean Anderson #define S2255_CMDBUF_SIZE 512 26747d8c881SDean Anderson __le32 *cmdbuf; 2680aa77f6cSMauro Carvalho Chehab }; 2690aa77f6cSMauro Carvalho Chehab 2700aa77f6cSMauro Carvalho Chehab static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) 2710aa77f6cSMauro Carvalho Chehab { 2720aa77f6cSMauro Carvalho Chehab return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); 2730aa77f6cSMauro Carvalho Chehab } 2740aa77f6cSMauro Carvalho Chehab 2750aa77f6cSMauro Carvalho Chehab struct s2255_fmt { 2760aa77f6cSMauro Carvalho Chehab u32 fourcc; 2770aa77f6cSMauro Carvalho Chehab int depth; 2780aa77f6cSMauro Carvalho Chehab }; 2790aa77f6cSMauro Carvalho Chehab 2800aa77f6cSMauro Carvalho Chehab /* buffer for one video frame */ 2810aa77f6cSMauro Carvalho Chehab struct s2255_buffer { 2820aa77f6cSMauro Carvalho Chehab /* common v4l buffer stuff -- must be first */ 2832d700715SJunghak Sung struct vb2_v4l2_buffer vb; 284340a30c5Ssensoray-dev struct list_head list; 2850aa77f6cSMauro Carvalho Chehab }; 2860aa77f6cSMauro Carvalho Chehab 2870aa77f6cSMauro Carvalho Chehab 2880aa77f6cSMauro Carvalho Chehab /* current cypress EEPROM firmware version */ 2890aa77f6cSMauro Carvalho Chehab #define S2255_CUR_USB_FWVER ((3 << 8) | 12) 2900aa77f6cSMauro Carvalho Chehab /* current DSP FW version */ 2910aa77f6cSMauro Carvalho Chehab #define S2255_CUR_DSP_FWVER 10104 2920aa77f6cSMauro Carvalho Chehab /* Need DSP version 5+ for video status feature */ 2930aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_STATUS 5 2940aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_COLORFILTER 8 295469af77aSHans Verkuil #define S2255_NORMS (V4L2_STD_ALL) 2960aa77f6cSMauro Carvalho Chehab 2970aa77f6cSMauro Carvalho Chehab /* private V4L2 controls */ 2980aa77f6cSMauro Carvalho Chehab 2990aa77f6cSMauro Carvalho Chehab /* 3000aa77f6cSMauro Carvalho Chehab * The following chart displays how COLORFILTER should be set 3010aa77f6cSMauro Carvalho Chehab * ========================================================= 3020aa77f6cSMauro Carvalho Chehab * = fourcc = COLORFILTER = 3030aa77f6cSMauro Carvalho Chehab * = =============================== 3040aa77f6cSMauro Carvalho Chehab * = = 0 = 1 = 3050aa77f6cSMauro Carvalho Chehab * ========================================================= 3060aa77f6cSMauro Carvalho Chehab * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= 3070aa77f6cSMauro Carvalho Chehab * = = s-video or = composite = 3080aa77f6cSMauro Carvalho Chehab * = = B/W camera = input = 3090aa77f6cSMauro Carvalho Chehab * ========================================================= 3100aa77f6cSMauro Carvalho Chehab * = other = color, svideo = color, = 3110aa77f6cSMauro Carvalho Chehab * = = = composite = 3120aa77f6cSMauro Carvalho Chehab * ========================================================= 3130aa77f6cSMauro Carvalho Chehab * 3140aa77f6cSMauro Carvalho Chehab * Notes: 3150aa77f6cSMauro Carvalho Chehab * channels 0-3 on 2255 are composite 3160aa77f6cSMauro Carvalho Chehab * channels 0-1 on 2257 are composite, 2-3 are s-video 3170aa77f6cSMauro Carvalho Chehab * If COLORFILTER is 0 with a composite color camera connected, 3180aa77f6cSMauro Carvalho Chehab * the output will appear monochrome but hatching 3190aa77f6cSMauro Carvalho Chehab * will occur. 3200aa77f6cSMauro Carvalho Chehab * COLORFILTER is different from "color killer" and "color effects" 3210aa77f6cSMauro Carvalho Chehab * for reasons above. 3220aa77f6cSMauro Carvalho Chehab */ 3230aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_ON 1 3240aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_OFF 0 325192f1e78SHans Verkuil #define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0) 3260aa77f6cSMauro Carvalho Chehab 3270aa77f6cSMauro Carvalho Chehab /* frame prefix size (sent once every frame) */ 3280aa77f6cSMauro Carvalho Chehab #define PREFIX_SIZE 512 3290aa77f6cSMauro Carvalho Chehab 3300aa77f6cSMauro Carvalho Chehab /* Channels on box are in reverse order */ 3310aa77f6cSMauro Carvalho Chehab static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; 3320aa77f6cSMauro Carvalho Chehab 3330aa77f6cSMauro Carvalho Chehab static int debug; 3340aa77f6cSMauro Carvalho Chehab 3350aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev); 3360aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev); 3375e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc); 3385e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc); 3395e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf, 3400aa77f6cSMauro Carvalho Chehab int jpgsize); 3415e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode); 3420aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev); 343e2a06704SDean A static void s2255_fwload_start(struct s2255_dev *dev); 3440aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev); 3450aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, 3460aa77f6cSMauro Carvalho Chehab u16 index, u16 value, void *buf, 3470aa77f6cSMauro Carvalho Chehab s32 buf_len, int bOut); 3480aa77f6cSMauro Carvalho Chehab 3490aa77f6cSMauro Carvalho Chehab /* dev_err macro with driver name */ 3500aa77f6cSMauro Carvalho Chehab #define S2255_DRIVER_NAME "s2255" 3510aa77f6cSMauro Carvalho Chehab #define s2255_dev_err(dev, fmt, arg...) \ 3520aa77f6cSMauro Carvalho Chehab dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) 3530aa77f6cSMauro Carvalho Chehab 354f5402007Ssensoray-dev #define dprintk(dev, level, fmt, arg...) \ 355f5402007Ssensoray-dev v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) 3560aa77f6cSMauro Carvalho Chehab 3570aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver; 3580aa77f6cSMauro Carvalho Chehab 3590aa77f6cSMauro Carvalho Chehab /* start video number */ 3600aa77f6cSMauro Carvalho Chehab static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ 3610aa77f6cSMauro Carvalho Chehab 3620aa77f6cSMauro Carvalho Chehab /* Enable jpeg capture. */ 3630aa77f6cSMauro Carvalho Chehab static int jpeg_enable = 1; 3640aa77f6cSMauro Carvalho Chehab 3650aa77f6cSMauro Carvalho Chehab module_param(debug, int, 0644); 3660aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); 3670aa77f6cSMauro Carvalho Chehab module_param(video_nr, int, 0644); 3680aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); 3690aa77f6cSMauro Carvalho Chehab module_param(jpeg_enable, int, 0644); 3700aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); 3710aa77f6cSMauro Carvalho Chehab 3720aa77f6cSMauro Carvalho Chehab /* USB device table */ 3730aa77f6cSMauro Carvalho Chehab #define USB_SENSORAY_VID 0x1943 3747fb2e072SArvind Yadav static const struct usb_device_id s2255_table[] = { 3750aa77f6cSMauro Carvalho Chehab {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, 3760aa77f6cSMauro Carvalho Chehab {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ 3770aa77f6cSMauro Carvalho Chehab { } /* Terminating entry */ 3780aa77f6cSMauro Carvalho Chehab }; 3790aa77f6cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, s2255_table); 3800aa77f6cSMauro Carvalho Chehab 3810aa77f6cSMauro Carvalho Chehab #define BUFFER_TIMEOUT msecs_to_jiffies(400) 3820aa77f6cSMauro Carvalho Chehab 3830aa77f6cSMauro Carvalho Chehab /* image formats. */ 3840aa77f6cSMauro Carvalho Chehab /* JPEG formats must be defined last to support jpeg_enable parameter */ 3850aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt formats[] = { 3860aa77f6cSMauro Carvalho Chehab { 3870aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_YUYV, 3880aa77f6cSMauro Carvalho Chehab .depth = 16 3890aa77f6cSMauro Carvalho Chehab 3900aa77f6cSMauro Carvalho Chehab }, { 3910aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_UYVY, 3920aa77f6cSMauro Carvalho Chehab .depth = 16 3930aa77f6cSMauro Carvalho Chehab }, { 3945c632b22SHans Verkuil .fourcc = V4L2_PIX_FMT_YUV422P, 3955c632b22SHans Verkuil .depth = 16 3965c632b22SHans Verkuil 3975c632b22SHans Verkuil }, { 3980aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_GREY, 3990aa77f6cSMauro Carvalho Chehab .depth = 8 4000aa77f6cSMauro Carvalho Chehab }, { 4010aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_JPEG, 4020aa77f6cSMauro Carvalho Chehab .depth = 24 4030aa77f6cSMauro Carvalho Chehab }, { 4040aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_MJPEG, 4050aa77f6cSMauro Carvalho Chehab .depth = 24 4060aa77f6cSMauro Carvalho Chehab } 4070aa77f6cSMauro Carvalho Chehab }; 4080aa77f6cSMauro Carvalho Chehab 4095e950fafSDean Anderson static int norm_maxw(struct s2255_vc *vc) 4100aa77f6cSMauro Carvalho Chehab { 4115e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4120aa77f6cSMauro Carvalho Chehab LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; 4130aa77f6cSMauro Carvalho Chehab } 4140aa77f6cSMauro Carvalho Chehab 4155e950fafSDean Anderson static int norm_maxh(struct s2255_vc *vc) 4160aa77f6cSMauro Carvalho Chehab { 4175e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4180aa77f6cSMauro Carvalho Chehab (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); 4190aa77f6cSMauro Carvalho Chehab } 4200aa77f6cSMauro Carvalho Chehab 4215e950fafSDean Anderson static int norm_minw(struct s2255_vc *vc) 4220aa77f6cSMauro Carvalho Chehab { 4235e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4240aa77f6cSMauro Carvalho Chehab LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; 4250aa77f6cSMauro Carvalho Chehab } 4260aa77f6cSMauro Carvalho Chehab 4275e950fafSDean Anderson static int norm_minh(struct s2255_vc *vc) 4280aa77f6cSMauro Carvalho Chehab { 4295e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4300aa77f6cSMauro Carvalho Chehab (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); 4310aa77f6cSMauro Carvalho Chehab } 4320aa77f6cSMauro Carvalho Chehab 4330aa77f6cSMauro Carvalho Chehab 4340aa77f6cSMauro Carvalho Chehab /* 4350aa77f6cSMauro Carvalho Chehab * TODO: fixme: move YUV reordering to hardware 4360aa77f6cSMauro Carvalho Chehab * converts 2255 planar format to yuyv or uyvy 4370aa77f6cSMauro Carvalho Chehab */ 4380aa77f6cSMauro Carvalho Chehab static void planar422p_to_yuv_packed(const unsigned char *in, 4390aa77f6cSMauro Carvalho Chehab unsigned char *out, 4400aa77f6cSMauro Carvalho Chehab int width, int height, 4410aa77f6cSMauro Carvalho Chehab int fmt) 4420aa77f6cSMauro Carvalho Chehab { 4430aa77f6cSMauro Carvalho Chehab unsigned char *pY; 4440aa77f6cSMauro Carvalho Chehab unsigned char *pCb; 4450aa77f6cSMauro Carvalho Chehab unsigned char *pCr; 4460aa77f6cSMauro Carvalho Chehab unsigned long size = height * width; 4470aa77f6cSMauro Carvalho Chehab unsigned int i; 4480aa77f6cSMauro Carvalho Chehab pY = (unsigned char *)in; 4490aa77f6cSMauro Carvalho Chehab pCr = (unsigned char *)in + height * width; 4500aa77f6cSMauro Carvalho Chehab pCb = (unsigned char *)in + height * width + (height * width / 2); 4510aa77f6cSMauro Carvalho Chehab for (i = 0; i < size * 2; i += 4) { 4520aa77f6cSMauro Carvalho Chehab out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; 4530aa77f6cSMauro Carvalho Chehab out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; 4540aa77f6cSMauro Carvalho Chehab out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; 4550aa77f6cSMauro Carvalho Chehab out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; 4560aa77f6cSMauro Carvalho Chehab } 4570aa77f6cSMauro Carvalho Chehab return; 4580aa77f6cSMauro Carvalho Chehab } 4590aa77f6cSMauro Carvalho Chehab 4600aa77f6cSMauro Carvalho Chehab static void s2255_reset_dsppower(struct s2255_dev *dev) 4610aa77f6cSMauro Carvalho Chehab { 4620aa77f6cSMauro Carvalho Chehab s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1); 463e2a06704SDean A msleep(50); 4640aa77f6cSMauro Carvalho Chehab s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); 4650aa77f6cSMauro Carvalho Chehab msleep(600); 4660aa77f6cSMauro Carvalho Chehab s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); 4670aa77f6cSMauro Carvalho Chehab return; 4680aa77f6cSMauro Carvalho Chehab } 4690aa77f6cSMauro Carvalho Chehab 4700aa77f6cSMauro Carvalho Chehab /* kickstarts the firmware loading. from probe 4710aa77f6cSMauro Carvalho Chehab */ 47274ee0477SKees Cook static void s2255_timer(struct timer_list *t) 4730aa77f6cSMauro Carvalho Chehab { 47474ee0477SKees Cook struct s2255_dev *dev = from_timer(dev, t, timer); 47574ee0477SKees Cook struct s2255_fw *data = dev->fw_data; 4760aa77f6cSMauro Carvalho Chehab if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { 477f5402007Ssensoray-dev pr_err("s2255: can't submit urb\n"); 4780aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 4790aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 4800aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 4810aa77f6cSMauro Carvalho Chehab return; 4820aa77f6cSMauro Carvalho Chehab } 4830aa77f6cSMauro Carvalho Chehab } 4840aa77f6cSMauro Carvalho Chehab 4850aa77f6cSMauro Carvalho Chehab 4860aa77f6cSMauro Carvalho Chehab /* this loads the firmware asynchronously. 4870b84caabSHans Verkuil Originally this was done synchronously in probe. 4880aa77f6cSMauro Carvalho Chehab But it is better to load it asynchronously here than block 4890aa77f6cSMauro Carvalho Chehab inside the probe function. Blocking inside probe affects boot time. 4900aa77f6cSMauro Carvalho Chehab FW loading is triggered by the timer in the probe function 4910aa77f6cSMauro Carvalho Chehab */ 4920aa77f6cSMauro Carvalho Chehab static void s2255_fwchunk_complete(struct urb *urb) 4930aa77f6cSMauro Carvalho Chehab { 4940aa77f6cSMauro Carvalho Chehab struct s2255_fw *data = urb->context; 4950aa77f6cSMauro Carvalho Chehab struct usb_device *udev = urb->dev; 4960aa77f6cSMauro Carvalho Chehab int len; 4970aa77f6cSMauro Carvalho Chehab if (urb->status) { 4980aa77f6cSMauro Carvalho Chehab dev_err(&udev->dev, "URB failed with status %d\n", urb->status); 4990aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 5000aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 5010aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 5020aa77f6cSMauro Carvalho Chehab return; 5030aa77f6cSMauro Carvalho Chehab } 5040aa77f6cSMauro Carvalho Chehab if (data->fw_urb == NULL) { 5050aa77f6cSMauro Carvalho Chehab s2255_dev_err(&udev->dev, "disconnected\n"); 5060aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 5070aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 5080aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 5090aa77f6cSMauro Carvalho Chehab return; 5100aa77f6cSMauro Carvalho Chehab } 5110aa77f6cSMauro Carvalho Chehab #define CHUNK_SIZE 512 5120aa77f6cSMauro Carvalho Chehab /* all USB transfers must be done with continuous kernel memory. 5130aa77f6cSMauro Carvalho Chehab can't allocate more than 128k in current linux kernel, so 5140aa77f6cSMauro Carvalho Chehab upload the firmware in chunks 5150aa77f6cSMauro Carvalho Chehab */ 5160aa77f6cSMauro Carvalho Chehab if (data->fw_loaded < data->fw_size) { 5170aa77f6cSMauro Carvalho Chehab len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? 5180aa77f6cSMauro Carvalho Chehab data->fw_size % CHUNK_SIZE : CHUNK_SIZE; 5190aa77f6cSMauro Carvalho Chehab 5200aa77f6cSMauro Carvalho Chehab if (len < CHUNK_SIZE) 5210aa77f6cSMauro Carvalho Chehab memset(data->pfw_data, 0, CHUNK_SIZE); 5220aa77f6cSMauro Carvalho Chehab 5230aa77f6cSMauro Carvalho Chehab memcpy(data->pfw_data, 5240aa77f6cSMauro Carvalho Chehab (char *) data->fw->data + data->fw_loaded, len); 5250aa77f6cSMauro Carvalho Chehab 5260aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), 5270aa77f6cSMauro Carvalho Chehab data->pfw_data, CHUNK_SIZE, 5280aa77f6cSMauro Carvalho Chehab s2255_fwchunk_complete, data); 5290aa77f6cSMauro Carvalho Chehab if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { 5300aa77f6cSMauro Carvalho Chehab dev_err(&udev->dev, "failed submit URB\n"); 5310aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 5320aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 5330aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 5340aa77f6cSMauro Carvalho Chehab return; 5350aa77f6cSMauro Carvalho Chehab } 5360aa77f6cSMauro Carvalho Chehab data->fw_loaded += len; 537f5402007Ssensoray-dev } else 5380aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); 5390aa77f6cSMauro Carvalho Chehab return; 5400aa77f6cSMauro Carvalho Chehab 5410aa77f6cSMauro Carvalho Chehab } 5420aa77f6cSMauro Carvalho Chehab 5439694fbecSsensoray-dev static void s2255_got_frame(struct s2255_vc *vc, int jpgsize) 5440aa77f6cSMauro Carvalho Chehab { 5450aa77f6cSMauro Carvalho Chehab struct s2255_buffer *buf; 5465e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 5470aa77f6cSMauro Carvalho Chehab unsigned long flags = 0; 5489694fbecSsensoray-dev 549340a30c5Ssensoray-dev spin_lock_irqsave(&vc->qlock, flags); 5505e950fafSDean Anderson if (list_empty(&vc->buf_list)) { 551f5402007Ssensoray-dev dprintk(dev, 1, "No active queue to serve\n"); 5529694fbecSsensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 5539694fbecSsensoray-dev return; 5540aa77f6cSMauro Carvalho Chehab } 5555e950fafSDean Anderson buf = list_entry(vc->buf_list.next, 556340a30c5Ssensoray-dev struct s2255_buffer, list); 557340a30c5Ssensoray-dev list_del(&buf->list); 558d6dd645eSJunghak Sung buf->vb.vb2_buf.timestamp = ktime_get_ns(); 5592d700715SJunghak Sung buf->vb.field = vc->field; 5602d700715SJunghak Sung buf->vb.sequence = vc->frame_count; 561340a30c5Ssensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 5629694fbecSsensoray-dev 5639694fbecSsensoray-dev s2255_fillbuff(vc, buf, jpgsize); 5649694fbecSsensoray-dev /* tell v4l buffer was filled */ 5652d700715SJunghak Sung vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 5669694fbecSsensoray-dev dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf); 5670aa77f6cSMauro Carvalho Chehab } 5680aa77f6cSMauro Carvalho Chehab 5690aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt *format_by_fourcc(int fourcc) 5700aa77f6cSMauro Carvalho Chehab { 5710aa77f6cSMauro Carvalho Chehab unsigned int i; 5720aa77f6cSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(formats); i++) { 5730aa77f6cSMauro Carvalho Chehab if (-1 == formats[i].fourcc) 5740aa77f6cSMauro Carvalho Chehab continue; 5750aa77f6cSMauro Carvalho Chehab if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || 5760aa77f6cSMauro Carvalho Chehab (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) 5770aa77f6cSMauro Carvalho Chehab continue; 5780aa77f6cSMauro Carvalho Chehab if (formats[i].fourcc == fourcc) 5790aa77f6cSMauro Carvalho Chehab return formats + i; 5800aa77f6cSMauro Carvalho Chehab } 5810aa77f6cSMauro Carvalho Chehab return NULL; 5820aa77f6cSMauro Carvalho Chehab } 5830aa77f6cSMauro Carvalho Chehab 5840aa77f6cSMauro Carvalho Chehab /* video buffer vmalloc implementation based partly on VIVI driver which is 5850aa77f6cSMauro Carvalho Chehab * Copyright (c) 2006 by 5860aa77f6cSMauro Carvalho Chehab * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> 5870aa77f6cSMauro Carvalho Chehab * Ted Walther <ted--a.t--enumera.com> 5880aa77f6cSMauro Carvalho Chehab * John Sokol <sokol--a.t--videotechnology.com> 5890aa77f6cSMauro Carvalho Chehab * http://v4l.videotechnology.com/ 5900aa77f6cSMauro Carvalho Chehab * 5910aa77f6cSMauro Carvalho Chehab */ 5925e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, 5930aa77f6cSMauro Carvalho Chehab struct s2255_buffer *buf, int jpgsize) 5940aa77f6cSMauro Carvalho Chehab { 5950aa77f6cSMauro Carvalho Chehab int pos = 0; 5960aa77f6cSMauro Carvalho Chehab const char *tmpbuf; 5972d700715SJunghak Sung char *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); 5980aa77f6cSMauro Carvalho Chehab unsigned long last_frame; 5995e950fafSDean Anderson struct s2255_dev *dev = vc->dev; 6000aa77f6cSMauro Carvalho Chehab 6010aa77f6cSMauro Carvalho Chehab if (!vbuf) 6020aa77f6cSMauro Carvalho Chehab return; 6035e950fafSDean Anderson last_frame = vc->last_frame; 6040aa77f6cSMauro Carvalho Chehab if (last_frame != -1) { 6050aa77f6cSMauro Carvalho Chehab tmpbuf = 6065e950fafSDean Anderson (const char *)vc->buffer.frame[last_frame].lpvbits; 6078bf405a0SDean Anderson switch (vc->fmt->fourcc) { 6080aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUYV: 6090aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_UYVY: 6100aa77f6cSMauro Carvalho Chehab planar422p_to_yuv_packed((const unsigned char *)tmpbuf, 611340a30c5Ssensoray-dev vbuf, vc->width, 612340a30c5Ssensoray-dev vc->height, 6138bf405a0SDean Anderson vc->fmt->fourcc); 6140aa77f6cSMauro Carvalho Chehab break; 6150aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_GREY: 616340a30c5Ssensoray-dev memcpy(vbuf, tmpbuf, vc->width * vc->height); 6170aa77f6cSMauro Carvalho Chehab break; 6180aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_JPEG: 6190aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_MJPEG: 6202d700715SJunghak Sung vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize); 621340a30c5Ssensoray-dev memcpy(vbuf, tmpbuf, jpgsize); 6220aa77f6cSMauro Carvalho Chehab break; 6230aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUV422P: 6240aa77f6cSMauro Carvalho Chehab memcpy(vbuf, tmpbuf, 625340a30c5Ssensoray-dev vc->width * vc->height * 2); 6260aa77f6cSMauro Carvalho Chehab break; 6270aa77f6cSMauro Carvalho Chehab default: 628f5402007Ssensoray-dev pr_info("s2255: unknown format?\n"); 6290aa77f6cSMauro Carvalho Chehab } 6305e950fafSDean Anderson vc->last_frame = -1; 6310aa77f6cSMauro Carvalho Chehab } else { 632f5402007Ssensoray-dev pr_err("s2255: =======no frame\n"); 6330aa77f6cSMauro Carvalho Chehab return; 6340aa77f6cSMauro Carvalho Chehab } 63586f181c7SMauro Carvalho Chehab dprintk(dev, 2, "s2255fill at : Buffer %p size= %d\n", 63686f181c7SMauro Carvalho Chehab vbuf, pos); 6370aa77f6cSMauro Carvalho Chehab } 6380aa77f6cSMauro Carvalho Chehab 6390aa77f6cSMauro Carvalho Chehab 6400aa77f6cSMauro Carvalho Chehab /* ------------------------------------------------------------------ 6410aa77f6cSMauro Carvalho Chehab Videobuf operations 6420aa77f6cSMauro Carvalho Chehab ------------------------------------------------------------------*/ 6430aa77f6cSMauro Carvalho Chehab 644df9ecb0cSHans Verkuil static int queue_setup(struct vb2_queue *vq, 645340a30c5Ssensoray-dev unsigned int *nbuffers, unsigned int *nplanes, 64636c0f8b3SHans Verkuil unsigned int sizes[], struct device *alloc_devs[]) 6470aa77f6cSMauro Carvalho Chehab { 648340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vq); 6499da62eb0SDean Anderson if (*nbuffers < S2255_MIN_BUFS) 6509da62eb0SDean Anderson *nbuffers = S2255_MIN_BUFS; 651340a30c5Ssensoray-dev *nplanes = 1; 652340a30c5Ssensoray-dev sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3); 6530aa77f6cSMauro Carvalho Chehab return 0; 6540aa77f6cSMauro Carvalho Chehab } 6550aa77f6cSMauro Carvalho Chehab 656340a30c5Ssensoray-dev static int buffer_prepare(struct vb2_buffer *vb) 6570aa77f6cSMauro Carvalho Chehab { 658340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); 6592d700715SJunghak Sung struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 6602d700715SJunghak Sung struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb); 6615e950fafSDean Anderson int w = vc->width; 6625e950fafSDean Anderson int h = vc->height; 663340a30c5Ssensoray-dev unsigned long size; 664340a30c5Ssensoray-dev 665340a30c5Ssensoray-dev dprintk(vc->dev, 4, "%s\n", __func__); 6665e950fafSDean Anderson if (vc->fmt == NULL) 6670aa77f6cSMauro Carvalho Chehab return -EINVAL; 6680aa77f6cSMauro Carvalho Chehab 6695e950fafSDean Anderson if ((w < norm_minw(vc)) || 6705e950fafSDean Anderson (w > norm_maxw(vc)) || 6715e950fafSDean Anderson (h < norm_minh(vc)) || 6725e950fafSDean Anderson (h > norm_maxh(vc))) { 67392cde477SDean Anderson dprintk(vc->dev, 4, "invalid buffer prepare\n"); 6740aa77f6cSMauro Carvalho Chehab return -EINVAL; 6750aa77f6cSMauro Carvalho Chehab } 676340a30c5Ssensoray-dev size = w * h * (vc->fmt->depth >> 3); 677340a30c5Ssensoray-dev if (vb2_plane_size(vb, 0) < size) { 67892cde477SDean Anderson dprintk(vc->dev, 4, "invalid buffer prepare\n"); 6790aa77f6cSMauro Carvalho Chehab return -EINVAL; 6800aa77f6cSMauro Carvalho Chehab } 6810aa77f6cSMauro Carvalho Chehab 6822d700715SJunghak Sung vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); 6830aa77f6cSMauro Carvalho Chehab return 0; 6840aa77f6cSMauro Carvalho Chehab } 6850aa77f6cSMauro Carvalho Chehab 686340a30c5Ssensoray-dev static void buffer_queue(struct vb2_buffer *vb) 6870aa77f6cSMauro Carvalho Chehab { 6882d700715SJunghak Sung struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 6892d700715SJunghak Sung struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb); 690340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); 691340a30c5Ssensoray-dev unsigned long flags = 0; 69292cde477SDean Anderson dprintk(vc->dev, 1, "%s\n", __func__); 693340a30c5Ssensoray-dev spin_lock_irqsave(&vc->qlock, flags); 694340a30c5Ssensoray-dev list_add_tail(&buf->list, &vc->buf_list); 695340a30c5Ssensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 6960aa77f6cSMauro Carvalho Chehab } 6970aa77f6cSMauro Carvalho Chehab 698340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count); 699e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq); 7000aa77f6cSMauro Carvalho Chehab 7011bc17717SJulia Lawall static const struct vb2_ops s2255_video_qops = { 702340a30c5Ssensoray-dev .queue_setup = queue_setup, 7030aa77f6cSMauro Carvalho Chehab .buf_prepare = buffer_prepare, 7040aa77f6cSMauro Carvalho Chehab .buf_queue = buffer_queue, 705340a30c5Ssensoray-dev .start_streaming = start_streaming, 706340a30c5Ssensoray-dev .stop_streaming = stop_streaming, 707340a30c5Ssensoray-dev .wait_prepare = vb2_ops_wait_prepare, 708340a30c5Ssensoray-dev .wait_finish = vb2_ops_wait_finish, 7090aa77f6cSMauro Carvalho Chehab }; 7100aa77f6cSMauro Carvalho Chehab 7110aa77f6cSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv, 7120aa77f6cSMauro Carvalho Chehab struct v4l2_capability *cap) 7130aa77f6cSMauro Carvalho Chehab { 714340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 715340a30c5Ssensoray-dev struct s2255_dev *dev = vc->dev; 71639696009SHans Verkuil 717c0decac1SMauro Carvalho Chehab strscpy(cap->driver, "s2255", sizeof(cap->driver)); 718c0decac1SMauro Carvalho Chehab strscpy(cap->card, "s2255", sizeof(cap->card)); 7190aa77f6cSMauro Carvalho Chehab usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 7200aa77f6cSMauro Carvalho Chehab return 0; 7210aa77f6cSMauro Carvalho Chehab } 7220aa77f6cSMauro Carvalho Chehab 7230aa77f6cSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 7240aa77f6cSMauro Carvalho Chehab struct v4l2_fmtdesc *f) 7250aa77f6cSMauro Carvalho Chehab { 7260aa77f6cSMauro Carvalho Chehab int index = f->index; 7270aa77f6cSMauro Carvalho Chehab 7280aa77f6cSMauro Carvalho Chehab if (index >= ARRAY_SIZE(formats)) 7290aa77f6cSMauro Carvalho Chehab return -EINVAL; 7300aa77f6cSMauro Carvalho Chehab if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || 7310aa77f6cSMauro Carvalho Chehab (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) 7320aa77f6cSMauro Carvalho Chehab return -EINVAL; 7330aa77f6cSMauro Carvalho Chehab f->pixelformat = formats[index].fourcc; 7340aa77f6cSMauro Carvalho Chehab return 0; 7350aa77f6cSMauro Carvalho Chehab } 7360aa77f6cSMauro Carvalho Chehab 7370aa77f6cSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 7380aa77f6cSMauro Carvalho Chehab struct v4l2_format *f) 7390aa77f6cSMauro Carvalho Chehab { 740340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 7415e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 7420aa77f6cSMauro Carvalho Chehab 7435e950fafSDean Anderson f->fmt.pix.width = vc->width; 7445e950fafSDean Anderson f->fmt.pix.height = vc->height; 74592513611SHans Verkuil if (f->fmt.pix.height >= 74692513611SHans Verkuil (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2) 74792513611SHans Verkuil f->fmt.pix.field = V4L2_FIELD_INTERLACED; 74892513611SHans Verkuil else 74992513611SHans Verkuil f->fmt.pix.field = V4L2_FIELD_TOP; 7505e950fafSDean Anderson f->fmt.pix.pixelformat = vc->fmt->fourcc; 7515e950fafSDean Anderson f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3); 7520aa77f6cSMauro Carvalho Chehab f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 75329ceb110SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 7540aa77f6cSMauro Carvalho Chehab return 0; 7550aa77f6cSMauro Carvalho Chehab } 7560aa77f6cSMauro Carvalho Chehab 7570aa77f6cSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 7580aa77f6cSMauro Carvalho Chehab struct v4l2_format *f) 7590aa77f6cSMauro Carvalho Chehab { 7600aa77f6cSMauro Carvalho Chehab const struct s2255_fmt *fmt; 7610aa77f6cSMauro Carvalho Chehab enum v4l2_field field; 762340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 7635e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 7640aa77f6cSMauro Carvalho Chehab 7650aa77f6cSMauro Carvalho Chehab fmt = format_by_fourcc(f->fmt.pix.pixelformat); 7660aa77f6cSMauro Carvalho Chehab 7670aa77f6cSMauro Carvalho Chehab if (fmt == NULL) 7680aa77f6cSMauro Carvalho Chehab return -EINVAL; 7690aa77f6cSMauro Carvalho Chehab 7700aa77f6cSMauro Carvalho Chehab field = f->fmt.pix.field; 7710aa77f6cSMauro Carvalho Chehab 77292cde477SDean Anderson dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n", 7730aa77f6cSMauro Carvalho Chehab __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); 7740aa77f6cSMauro Carvalho Chehab if (is_ntsc) { 7750aa77f6cSMauro Carvalho Chehab /* NTSC */ 7760aa77f6cSMauro Carvalho Chehab if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { 7770aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; 77892513611SHans Verkuil field = V4L2_FIELD_INTERLACED; 7790aa77f6cSMauro Carvalho Chehab } else { 7800aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; 7810aa77f6cSMauro Carvalho Chehab field = V4L2_FIELD_TOP; 7820aa77f6cSMauro Carvalho Chehab } 7830aa77f6cSMauro Carvalho Chehab if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) 7840aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; 7850aa77f6cSMauro Carvalho Chehab else 7860aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; 7870aa77f6cSMauro Carvalho Chehab } else { 7880aa77f6cSMauro Carvalho Chehab /* PAL */ 7890aa77f6cSMauro Carvalho Chehab if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { 7900aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; 79192513611SHans Verkuil field = V4L2_FIELD_INTERLACED; 7920aa77f6cSMauro Carvalho Chehab } else { 7930aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_PAL; 7940aa77f6cSMauro Carvalho Chehab field = V4L2_FIELD_TOP; 7950aa77f6cSMauro Carvalho Chehab } 79692513611SHans Verkuil if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) 7970aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_4CIFS_PAL; 79892513611SHans Verkuil else 7990aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_1CIFS_PAL; 8000aa77f6cSMauro Carvalho Chehab } 8010aa77f6cSMauro Carvalho Chehab f->fmt.pix.field = field; 8020aa77f6cSMauro Carvalho Chehab f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 8030aa77f6cSMauro Carvalho Chehab f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 80429ceb110SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 80592cde477SDean Anderson dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__, 8060aa77f6cSMauro Carvalho Chehab f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); 8070aa77f6cSMauro Carvalho Chehab return 0; 8080aa77f6cSMauro Carvalho Chehab } 8090aa77f6cSMauro Carvalho Chehab 8100aa77f6cSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 8110aa77f6cSMauro Carvalho Chehab struct v4l2_format *f) 8120aa77f6cSMauro Carvalho Chehab { 813340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 8140aa77f6cSMauro Carvalho Chehab const struct s2255_fmt *fmt; 815340a30c5Ssensoray-dev struct vb2_queue *q = &vc->vb_vidq; 8160aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 8170aa77f6cSMauro Carvalho Chehab int ret; 8180aa77f6cSMauro Carvalho Chehab 819340a30c5Ssensoray-dev ret = vidioc_try_fmt_vid_cap(file, vc, f); 8200aa77f6cSMauro Carvalho Chehab 8210aa77f6cSMauro Carvalho Chehab if (ret < 0) 8220aa77f6cSMauro Carvalho Chehab return ret; 8230aa77f6cSMauro Carvalho Chehab 8240aa77f6cSMauro Carvalho Chehab fmt = format_by_fourcc(f->fmt.pix.pixelformat); 8250aa77f6cSMauro Carvalho Chehab 8260aa77f6cSMauro Carvalho Chehab if (fmt == NULL) 8270aa77f6cSMauro Carvalho Chehab return -EINVAL; 8280aa77f6cSMauro Carvalho Chehab 829340a30c5Ssensoray-dev if (vb2_is_busy(q)) { 83092cde477SDean Anderson dprintk(vc->dev, 1, "queue busy\n"); 831340a30c5Ssensoray-dev return -EBUSY; 8320aa77f6cSMauro Carvalho Chehab } 8330aa77f6cSMauro Carvalho Chehab 8345e950fafSDean Anderson mode = vc->mode; 8355e950fafSDean Anderson vc->fmt = fmt; 8365e950fafSDean Anderson vc->width = f->fmt.pix.width; 8375e950fafSDean Anderson vc->height = f->fmt.pix.height; 838340a30c5Ssensoray-dev vc->field = f->fmt.pix.field; 8395e950fafSDean Anderson if (vc->width > norm_minw(vc)) { 8405e950fafSDean Anderson if (vc->height > norm_minh(vc)) { 8415e950fafSDean Anderson if (vc->cap_parm.capturemode & 8420aa77f6cSMauro Carvalho Chehab V4L2_MODE_HIGHQUALITY) 8430aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_4CIFSI; 8440aa77f6cSMauro Carvalho Chehab else 8450aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_4CIFS; 8460aa77f6cSMauro Carvalho Chehab } else 8470aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_2CIFS; 8480aa77f6cSMauro Carvalho Chehab 8490aa77f6cSMauro Carvalho Chehab } else { 8500aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_1CIFS; 8510aa77f6cSMauro Carvalho Chehab } 8520aa77f6cSMauro Carvalho Chehab /* color mode */ 8535e950fafSDean Anderson switch (vc->fmt->fourcc) { 8540aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_GREY: 8550aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8560aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_Y8; 8570aa77f6cSMauro Carvalho Chehab break; 8580aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_JPEG: 8590aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_MJPEG: 8600aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8610aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_JPG; 8625e950fafSDean Anderson mode.color |= (vc->jpegqual << 8); 8630aa77f6cSMauro Carvalho Chehab break; 8640aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUV422P: 8650aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8660aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_YUVPL; 8670aa77f6cSMauro Carvalho Chehab break; 8680aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUYV: 8690aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_UYVY: 8700aa77f6cSMauro Carvalho Chehab default: 8710aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8720aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_YUVPK; 8730aa77f6cSMauro Carvalho Chehab break; 8740aa77f6cSMauro Carvalho Chehab } 8755e950fafSDean Anderson if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR)) 8760aa77f6cSMauro Carvalho Chehab mode.restart = 1; 8775e950fafSDean Anderson else if (mode.scale != vc->mode.scale) 8780aa77f6cSMauro Carvalho Chehab mode.restart = 1; 8795e950fafSDean Anderson else if (mode.format != vc->mode.format) 8800aa77f6cSMauro Carvalho Chehab mode.restart = 1; 8815e950fafSDean Anderson vc->mode = mode; 8825e950fafSDean Anderson (void) s2255_set_mode(vc, &mode); 883340a30c5Ssensoray-dev return 0; 8840aa77f6cSMauro Carvalho Chehab } 8850aa77f6cSMauro Carvalho Chehab 8860aa77f6cSMauro Carvalho Chehab 8870aa77f6cSMauro Carvalho Chehab /* write to the configuration pipe, synchronously */ 8880aa77f6cSMauro Carvalho Chehab static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, 8890aa77f6cSMauro Carvalho Chehab int size) 8900aa77f6cSMauro Carvalho Chehab { 8910aa77f6cSMauro Carvalho Chehab int pipe; 8920aa77f6cSMauro Carvalho Chehab int done; 8930aa77f6cSMauro Carvalho Chehab long retval = -1; 8940aa77f6cSMauro Carvalho Chehab if (udev) { 8950aa77f6cSMauro Carvalho Chehab pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); 8960aa77f6cSMauro Carvalho Chehab retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); 8970aa77f6cSMauro Carvalho Chehab } 8980aa77f6cSMauro Carvalho Chehab return retval; 8990aa77f6cSMauro Carvalho Chehab } 9000aa77f6cSMauro Carvalho Chehab 9010aa77f6cSMauro Carvalho Chehab static u32 get_transfer_size(struct s2255_mode *mode) 9020aa77f6cSMauro Carvalho Chehab { 9030aa77f6cSMauro Carvalho Chehab int linesPerFrame = LINE_SZ_DEF; 9040aa77f6cSMauro Carvalho Chehab int pixelsPerLine = NUM_LINES_DEF; 9050aa77f6cSMauro Carvalho Chehab u32 outImageSize; 9060aa77f6cSMauro Carvalho Chehab u32 usbInSize; 9070aa77f6cSMauro Carvalho Chehab unsigned int mask_mult; 9080aa77f6cSMauro Carvalho Chehab 9090aa77f6cSMauro Carvalho Chehab if (mode == NULL) 9100aa77f6cSMauro Carvalho Chehab return 0; 9110aa77f6cSMauro Carvalho Chehab 9120aa77f6cSMauro Carvalho Chehab if (mode->format == FORMAT_NTSC) { 9130aa77f6cSMauro Carvalho Chehab switch (mode->scale) { 9140aa77f6cSMauro Carvalho Chehab case SCALE_4CIFS: 9150aa77f6cSMauro Carvalho Chehab case SCALE_4CIFSI: 9160aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; 9170aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_4CIFS_NTSC; 9180aa77f6cSMauro Carvalho Chehab break; 9190aa77f6cSMauro Carvalho Chehab case SCALE_2CIFS: 9200aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_2CIFS_NTSC; 9210aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_2CIFS_NTSC; 9220aa77f6cSMauro Carvalho Chehab break; 9230aa77f6cSMauro Carvalho Chehab case SCALE_1CIFS: 9240aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_1CIFS_NTSC; 9250aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_1CIFS_NTSC; 9260aa77f6cSMauro Carvalho Chehab break; 9270aa77f6cSMauro Carvalho Chehab default: 9280aa77f6cSMauro Carvalho Chehab break; 9290aa77f6cSMauro Carvalho Chehab } 9300aa77f6cSMauro Carvalho Chehab } else if (mode->format == FORMAT_PAL) { 9310aa77f6cSMauro Carvalho Chehab switch (mode->scale) { 9320aa77f6cSMauro Carvalho Chehab case SCALE_4CIFS: 9330aa77f6cSMauro Carvalho Chehab case SCALE_4CIFSI: 9340aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_4CIFS_PAL * 2; 9350aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_4CIFS_PAL; 9360aa77f6cSMauro Carvalho Chehab break; 9370aa77f6cSMauro Carvalho Chehab case SCALE_2CIFS: 9380aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_2CIFS_PAL; 9390aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_2CIFS_PAL; 9400aa77f6cSMauro Carvalho Chehab break; 9410aa77f6cSMauro Carvalho Chehab case SCALE_1CIFS: 9420aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_1CIFS_PAL; 9430aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_1CIFS_PAL; 9440aa77f6cSMauro Carvalho Chehab break; 9450aa77f6cSMauro Carvalho Chehab default: 9460aa77f6cSMauro Carvalho Chehab break; 9470aa77f6cSMauro Carvalho Chehab } 9480aa77f6cSMauro Carvalho Chehab } 9490aa77f6cSMauro Carvalho Chehab outImageSize = linesPerFrame * pixelsPerLine; 9500aa77f6cSMauro Carvalho Chehab if ((mode->color & MASK_COLOR) != COLOR_Y8) { 9510aa77f6cSMauro Carvalho Chehab /* 2 bytes/pixel if not monochrome */ 9520aa77f6cSMauro Carvalho Chehab outImageSize *= 2; 9530aa77f6cSMauro Carvalho Chehab } 9540aa77f6cSMauro Carvalho Chehab 9550aa77f6cSMauro Carvalho Chehab /* total bytes to send including prefix and 4K padding; 9560aa77f6cSMauro Carvalho Chehab must be a multiple of USB_READ_SIZE */ 9570aa77f6cSMauro Carvalho Chehab usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ 9580aa77f6cSMauro Carvalho Chehab mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; 9590aa77f6cSMauro Carvalho Chehab /* if size not a multiple of USB_READ_SIZE */ 9600aa77f6cSMauro Carvalho Chehab if (usbInSize & ~mask_mult) 9610aa77f6cSMauro Carvalho Chehab usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); 9620aa77f6cSMauro Carvalho Chehab return usbInSize; 9630aa77f6cSMauro Carvalho Chehab } 9640aa77f6cSMauro Carvalho Chehab 9650aa77f6cSMauro Carvalho Chehab static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) 9660aa77f6cSMauro Carvalho Chehab { 9670aa77f6cSMauro Carvalho Chehab struct device *dev = &sdev->udev->dev; 9680aa77f6cSMauro Carvalho Chehab dev_info(dev, "------------------------------------------------\n"); 9690aa77f6cSMauro Carvalho Chehab dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); 9700aa77f6cSMauro Carvalho Chehab dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); 9710aa77f6cSMauro Carvalho Chehab dev_info(dev, "bright: 0x%x\n", mode->bright); 9720aa77f6cSMauro Carvalho Chehab dev_info(dev, "------------------------------------------------\n"); 9730aa77f6cSMauro Carvalho Chehab } 9740aa77f6cSMauro Carvalho Chehab 9750aa77f6cSMauro Carvalho Chehab /* 9760aa77f6cSMauro Carvalho Chehab * set mode is the function which controls the DSP. 9770aa77f6cSMauro Carvalho Chehab * the restart parameter in struct s2255_mode should be set whenever 9780aa77f6cSMauro Carvalho Chehab * the image size could change via color format, video system or image 9790aa77f6cSMauro Carvalho Chehab * size. 9800aa77f6cSMauro Carvalho Chehab * When the restart parameter is set, we sleep for ONE frame to allow the 9810aa77f6cSMauro Carvalho Chehab * DSP time to get the new frame 9820aa77f6cSMauro Carvalho Chehab */ 9835e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, 9840aa77f6cSMauro Carvalho Chehab struct s2255_mode *mode) 9850aa77f6cSMauro Carvalho Chehab { 9860aa77f6cSMauro Carvalho Chehab int res; 9870aa77f6cSMauro Carvalho Chehab unsigned long chn_rev; 9885e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 9890b84caabSHans Verkuil int i; 99047d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 9910b84caabSHans Verkuil 99247d8c881SDean Anderson mutex_lock(&dev->cmdlock); 9935e950fafSDean Anderson chn_rev = G_chnmap[vc->idx]; 9945e950fafSDean Anderson dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx); 9950aa77f6cSMauro Carvalho Chehab /* if JPEG, set the quality */ 9960aa77f6cSMauro Carvalho Chehab if ((mode->color & MASK_COLOR) == COLOR_JPG) { 9970aa77f6cSMauro Carvalho Chehab mode->color &= ~MASK_COLOR; 9980aa77f6cSMauro Carvalho Chehab mode->color |= COLOR_JPG; 9990aa77f6cSMauro Carvalho Chehab mode->color &= ~MASK_JPG_QUALITY; 10005e950fafSDean Anderson mode->color |= (vc->jpegqual << 8); 10010aa77f6cSMauro Carvalho Chehab } 10020aa77f6cSMauro Carvalho Chehab /* save the mode */ 10035e950fafSDean Anderson vc->mode = *mode; 10045e950fafSDean Anderson vc->req_image_size = get_transfer_size(mode); 10055e950fafSDean Anderson dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size); 10060aa77f6cSMauro Carvalho Chehab /* set the mode */ 10070aa77f6cSMauro Carvalho Chehab buffer[0] = IN_DATA_TOKEN; 10080aa77f6cSMauro Carvalho Chehab buffer[1] = (__le32) cpu_to_le32(chn_rev); 10090aa77f6cSMauro Carvalho Chehab buffer[2] = CMD_SET_MODE; 10100b84caabSHans Verkuil for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++) 10115e950fafSDean Anderson buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]); 10125e950fafSDean Anderson vc->setmode_ready = 0; 10130aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 10140aa77f6cSMauro Carvalho Chehab if (debug) 10150aa77f6cSMauro Carvalho Chehab s2255_print_cfg(dev, mode); 10160aa77f6cSMauro Carvalho Chehab /* wait at least 3 frames before continuing */ 10170aa77f6cSMauro Carvalho Chehab if (mode->restart) { 10185e950fafSDean Anderson wait_event_timeout(vc->wait_setmode, 10195e950fafSDean Anderson (vc->setmode_ready != 0), 10200aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); 10215e950fafSDean Anderson if (vc->setmode_ready != 1) { 1022f5402007Ssensoray-dev dprintk(dev, 0, "s2255: no set mode response\n"); 10230aa77f6cSMauro Carvalho Chehab res = -EFAULT; 10240aa77f6cSMauro Carvalho Chehab } 10250aa77f6cSMauro Carvalho Chehab } 10260aa77f6cSMauro Carvalho Chehab /* clear the restart flag */ 10275e950fafSDean Anderson vc->mode.restart = 0; 10285e950fafSDean Anderson dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res); 102947d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 10300aa77f6cSMauro Carvalho Chehab return res; 10310aa77f6cSMauro Carvalho Chehab } 10320aa77f6cSMauro Carvalho Chehab 10335e950fafSDean Anderson static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus) 10340aa77f6cSMauro Carvalho Chehab { 10350aa77f6cSMauro Carvalho Chehab int res; 10360aa77f6cSMauro Carvalho Chehab u32 chn_rev; 10375e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 103847d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 103947d8c881SDean Anderson 104047d8c881SDean Anderson mutex_lock(&dev->cmdlock); 10415e950fafSDean Anderson chn_rev = G_chnmap[vc->idx]; 10425e950fafSDean Anderson dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx); 10430aa77f6cSMauro Carvalho Chehab /* form the get vid status command */ 10440aa77f6cSMauro Carvalho Chehab buffer[0] = IN_DATA_TOKEN; 10450aa77f6cSMauro Carvalho Chehab buffer[1] = (__le32) cpu_to_le32(chn_rev); 10460aa77f6cSMauro Carvalho Chehab buffer[2] = CMD_STATUS; 10470aa77f6cSMauro Carvalho Chehab *pstatus = 0; 10485e950fafSDean Anderson vc->vidstatus_ready = 0; 10490aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 10505e950fafSDean Anderson wait_event_timeout(vc->wait_vidstatus, 10515e950fafSDean Anderson (vc->vidstatus_ready != 0), 10520aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); 10535e950fafSDean Anderson if (vc->vidstatus_ready != 1) { 1054f5402007Ssensoray-dev dprintk(dev, 0, "s2255: no vidstatus response\n"); 10550aa77f6cSMauro Carvalho Chehab res = -EFAULT; 10560aa77f6cSMauro Carvalho Chehab } 10575e950fafSDean Anderson *pstatus = vc->vidstatus; 1058f5402007Ssensoray-dev dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus); 105947d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 10600aa77f6cSMauro Carvalho Chehab return res; 10610aa77f6cSMauro Carvalho Chehab } 10620aa77f6cSMauro Carvalho Chehab 1063340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count) 10640aa77f6cSMauro Carvalho Chehab { 1065340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vq); 10660aa77f6cSMauro Carvalho Chehab int j; 106792cde477SDean Anderson 10685e950fafSDean Anderson vc->last_frame = -1; 10695e950fafSDean Anderson vc->bad_payload = 0; 10705e950fafSDean Anderson vc->cur_frame = 0; 10715e950fafSDean Anderson vc->frame_count = 0; 10720aa77f6cSMauro Carvalho Chehab for (j = 0; j < SYS_FRAMES; j++) { 10735e950fafSDean Anderson vc->buffer.frame[j].ulState = S2255_READ_IDLE; 10745e950fafSDean Anderson vc->buffer.frame[j].cur_size = 0; 10750aa77f6cSMauro Carvalho Chehab } 1076340a30c5Ssensoray-dev return s2255_start_acquire(vc); 10770aa77f6cSMauro Carvalho Chehab } 10780aa77f6cSMauro Carvalho Chehab 1079340a30c5Ssensoray-dev /* abort streaming and wait for last buffer */ 1080e37559b2SHans Verkuil static void stop_streaming(struct vb2_queue *vq) 10810aa77f6cSMauro Carvalho Chehab { 1082340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vq); 1083340a30c5Ssensoray-dev struct s2255_buffer *buf, *node; 1084340a30c5Ssensoray-dev unsigned long flags; 1085340a30c5Ssensoray-dev (void) s2255_stop_acquire(vc); 1086340a30c5Ssensoray-dev spin_lock_irqsave(&vc->qlock, flags); 1087340a30c5Ssensoray-dev list_for_each_entry_safe(buf, node, &vc->buf_list, list) { 1088340a30c5Ssensoray-dev list_del(&buf->list); 10892d700715SJunghak Sung vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 1090340a30c5Ssensoray-dev dprintk(vc->dev, 2, "[%p/%d] done\n", 10912d700715SJunghak Sung buf, buf->vb.vb2_buf.index); 1092340a30c5Ssensoray-dev } 1093340a30c5Ssensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 10940aa77f6cSMauro Carvalho Chehab } 10950aa77f6cSMauro Carvalho Chehab 1096314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) 10970aa77f6cSMauro Carvalho Chehab { 1098340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 10990aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 1100340a30c5Ssensoray-dev struct vb2_queue *q = &vc->vb_vidq; 1101469af77aSHans Verkuil 1102340a30c5Ssensoray-dev /* 1103340a30c5Ssensoray-dev * Changing the standard implies a format change, which is not allowed 1104340a30c5Ssensoray-dev * while buffers for use with streaming have already been allocated. 1105340a30c5Ssensoray-dev */ 1106340a30c5Ssensoray-dev if (vb2_is_busy(q)) 1107340a30c5Ssensoray-dev return -EBUSY; 1108340a30c5Ssensoray-dev 110992cde477SDean Anderson mode = vc->mode; 1110314527acSHans Verkuil if (i & V4L2_STD_525_60) { 111192cde477SDean Anderson dprintk(vc->dev, 4, "%s 60 Hz\n", __func__); 11120aa77f6cSMauro Carvalho Chehab /* if changing format, reset frame decimation/intervals */ 11130aa77f6cSMauro Carvalho Chehab if (mode.format != FORMAT_NTSC) { 11140aa77f6cSMauro Carvalho Chehab mode.restart = 1; 11150aa77f6cSMauro Carvalho Chehab mode.format = FORMAT_NTSC; 11160aa77f6cSMauro Carvalho Chehab mode.fdec = FDEC_1; 11175e950fafSDean Anderson vc->width = LINE_SZ_4CIFS_NTSC; 11185e950fafSDean Anderson vc->height = NUM_LINES_4CIFS_NTSC * 2; 11190aa77f6cSMauro Carvalho Chehab } 1120314527acSHans Verkuil } else if (i & V4L2_STD_625_50) { 112192cde477SDean Anderson dprintk(vc->dev, 4, "%s 50 Hz\n", __func__); 11220aa77f6cSMauro Carvalho Chehab if (mode.format != FORMAT_PAL) { 11230aa77f6cSMauro Carvalho Chehab mode.restart = 1; 11240aa77f6cSMauro Carvalho Chehab mode.format = FORMAT_PAL; 11250aa77f6cSMauro Carvalho Chehab mode.fdec = FDEC_1; 11265e950fafSDean Anderson vc->width = LINE_SZ_4CIFS_PAL; 11275e950fafSDean Anderson vc->height = NUM_LINES_4CIFS_PAL * 2; 11280aa77f6cSMauro Carvalho Chehab } 1129340a30c5Ssensoray-dev } else 1130340a30c5Ssensoray-dev return -EINVAL; 113192cde477SDean Anderson vc->std = i; 11320aa77f6cSMauro Carvalho Chehab if (mode.restart) 113392cde477SDean Anderson s2255_set_mode(vc, &mode); 1134340a30c5Ssensoray-dev return 0; 11350aa77f6cSMauro Carvalho Chehab } 11360aa77f6cSMauro Carvalho Chehab 1137469af77aSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i) 1138469af77aSHans Verkuil { 1139340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 1140469af77aSHans Verkuil 114192cde477SDean Anderson *i = vc->std; 1142469af77aSHans Verkuil return 0; 1143469af77aSHans Verkuil } 1144469af77aSHans Verkuil 11450aa77f6cSMauro Carvalho Chehab /* Sensoray 2255 is a multiple channel capture device. 11460aa77f6cSMauro Carvalho Chehab It does not have a "crossbar" of inputs. 11470aa77f6cSMauro Carvalho Chehab We use one V4L device per channel. The user must 11480aa77f6cSMauro Carvalho Chehab be aware that certain combinations are not allowed. 11490aa77f6cSMauro Carvalho Chehab For instance, you cannot do full FPS on more than 2 channels(2 videodevs) 11500aa77f6cSMauro Carvalho Chehab at once in color(you can do full fps on 4 channels with greyscale. 11510aa77f6cSMauro Carvalho Chehab */ 11520aa77f6cSMauro Carvalho Chehab static int vidioc_enum_input(struct file *file, void *priv, 11530aa77f6cSMauro Carvalho Chehab struct v4l2_input *inp) 11540aa77f6cSMauro Carvalho Chehab { 1155340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 115692cde477SDean Anderson struct s2255_dev *dev = vc->dev; 11570aa77f6cSMauro Carvalho Chehab u32 status = 0; 115892cde477SDean Anderson 11590aa77f6cSMauro Carvalho Chehab if (inp->index != 0) 11600aa77f6cSMauro Carvalho Chehab return -EINVAL; 11610aa77f6cSMauro Carvalho Chehab inp->type = V4L2_INPUT_TYPE_CAMERA; 11620aa77f6cSMauro Carvalho Chehab inp->std = S2255_NORMS; 11630aa77f6cSMauro Carvalho Chehab inp->status = 0; 11640aa77f6cSMauro Carvalho Chehab if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { 11650aa77f6cSMauro Carvalho Chehab int rc; 116692cde477SDean Anderson rc = s2255_cmd_status(vc, &status); 1167f5402007Ssensoray-dev dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n", 1168f5402007Ssensoray-dev rc, status); 11690aa77f6cSMauro Carvalho Chehab if (rc == 0) 11700aa77f6cSMauro Carvalho Chehab inp->status = (status & 0x01) ? 0 11710aa77f6cSMauro Carvalho Chehab : V4L2_IN_ST_NO_SIGNAL; 11720aa77f6cSMauro Carvalho Chehab } 11730aa77f6cSMauro Carvalho Chehab switch (dev->pid) { 11740aa77f6cSMauro Carvalho Chehab case 0x2255: 11750aa77f6cSMauro Carvalho Chehab default: 1176c0decac1SMauro Carvalho Chehab strscpy(inp->name, "Composite", sizeof(inp->name)); 11770aa77f6cSMauro Carvalho Chehab break; 11780aa77f6cSMauro Carvalho Chehab case 0x2257: 1179c0decac1SMauro Carvalho Chehab strscpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video", 11800aa77f6cSMauro Carvalho Chehab sizeof(inp->name)); 11810aa77f6cSMauro Carvalho Chehab break; 11820aa77f6cSMauro Carvalho Chehab } 11830aa77f6cSMauro Carvalho Chehab return 0; 11840aa77f6cSMauro Carvalho Chehab } 11850aa77f6cSMauro Carvalho Chehab 11860aa77f6cSMauro Carvalho Chehab static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 11870aa77f6cSMauro Carvalho Chehab { 11880aa77f6cSMauro Carvalho Chehab *i = 0; 11890aa77f6cSMauro Carvalho Chehab return 0; 11900aa77f6cSMauro Carvalho Chehab } 11910aa77f6cSMauro Carvalho Chehab static int vidioc_s_input(struct file *file, void *priv, unsigned int i) 11920aa77f6cSMauro Carvalho Chehab { 11930aa77f6cSMauro Carvalho Chehab if (i > 0) 11940aa77f6cSMauro Carvalho Chehab return -EINVAL; 11950aa77f6cSMauro Carvalho Chehab return 0; 11960aa77f6cSMauro Carvalho Chehab } 11970aa77f6cSMauro Carvalho Chehab 1198192f1e78SHans Verkuil static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) 11990aa77f6cSMauro Carvalho Chehab { 12005e950fafSDean Anderson struct s2255_vc *vc = 12015e950fafSDean Anderson container_of(ctrl->handler, struct s2255_vc, hdl); 12020aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 12035e950fafSDean Anderson mode = vc->mode; 12040aa77f6cSMauro Carvalho Chehab /* update the mode to the corresponding value */ 12050aa77f6cSMauro Carvalho Chehab switch (ctrl->id) { 12060aa77f6cSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 1207192f1e78SHans Verkuil mode.bright = ctrl->val; 12080aa77f6cSMauro Carvalho Chehab break; 12090aa77f6cSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 1210192f1e78SHans Verkuil mode.contrast = ctrl->val; 12110aa77f6cSMauro Carvalho Chehab break; 12120aa77f6cSMauro Carvalho Chehab case V4L2_CID_HUE: 1213192f1e78SHans Verkuil mode.hue = ctrl->val; 12140aa77f6cSMauro Carvalho Chehab break; 12150aa77f6cSMauro Carvalho Chehab case V4L2_CID_SATURATION: 1216192f1e78SHans Verkuil mode.saturation = ctrl->val; 12170aa77f6cSMauro Carvalho Chehab break; 1218192f1e78SHans Verkuil case V4L2_CID_S2255_COLORFILTER: 12190aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_INPUT_TYPE; 1220192f1e78SHans Verkuil mode.color |= !ctrl->val << 16; 12210aa77f6cSMauro Carvalho Chehab break; 12227041dec7SHans Verkuil case V4L2_CID_JPEG_COMPRESSION_QUALITY: 12235e950fafSDean Anderson vc->jpegqual = ctrl->val; 12247041dec7SHans Verkuil return 0; 12250aa77f6cSMauro Carvalho Chehab default: 12260aa77f6cSMauro Carvalho Chehab return -EINVAL; 12270aa77f6cSMauro Carvalho Chehab } 12280aa77f6cSMauro Carvalho Chehab mode.restart = 0; 12290aa77f6cSMauro Carvalho Chehab /* set mode here. Note: stream does not need restarted. 12300aa77f6cSMauro Carvalho Chehab some V4L programs restart stream unnecessarily 12310aa77f6cSMauro Carvalho Chehab after a s_crtl. 12320aa77f6cSMauro Carvalho Chehab */ 12335e950fafSDean Anderson s2255_set_mode(vc, &mode); 12340aa77f6cSMauro Carvalho Chehab return 0; 12350aa77f6cSMauro Carvalho Chehab } 12360aa77f6cSMauro Carvalho Chehab 12370aa77f6cSMauro Carvalho Chehab static int vidioc_g_jpegcomp(struct file *file, void *priv, 12380aa77f6cSMauro Carvalho Chehab struct v4l2_jpegcompression *jc) 12390aa77f6cSMauro Carvalho Chehab { 1240340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 12417041dec7SHans Verkuil 12427041dec7SHans Verkuil memset(jc, 0, sizeof(*jc)); 12435e950fafSDean Anderson jc->quality = vc->jpegqual; 124492cde477SDean Anderson dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality); 12450aa77f6cSMauro Carvalho Chehab return 0; 12460aa77f6cSMauro Carvalho Chehab } 12470aa77f6cSMauro Carvalho Chehab 12480aa77f6cSMauro Carvalho Chehab static int vidioc_s_jpegcomp(struct file *file, void *priv, 1249d88aab53SHans Verkuil const struct v4l2_jpegcompression *jc) 12500aa77f6cSMauro Carvalho Chehab { 1251340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 1252340a30c5Ssensoray-dev 12530aa77f6cSMauro Carvalho Chehab if (jc->quality < 0 || jc->quality > 100) 12540aa77f6cSMauro Carvalho Chehab return -EINVAL; 12555e950fafSDean Anderson v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality); 125692cde477SDean Anderson dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality); 12570aa77f6cSMauro Carvalho Chehab return 0; 12580aa77f6cSMauro Carvalho Chehab } 12590aa77f6cSMauro Carvalho Chehab 12600aa77f6cSMauro Carvalho Chehab static int vidioc_g_parm(struct file *file, void *priv, 12610aa77f6cSMauro Carvalho Chehab struct v4l2_streamparm *sp) 12620aa77f6cSMauro Carvalho Chehab { 12630aa77f6cSMauro Carvalho Chehab __u32 def_num, def_dem; 1264340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 1265340a30c5Ssensoray-dev 12660aa77f6cSMauro Carvalho Chehab if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 12670aa77f6cSMauro Carvalho Chehab return -EINVAL; 12680aa77f6cSMauro Carvalho Chehab sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 12695e950fafSDean Anderson sp->parm.capture.capturemode = vc->cap_parm.capturemode; 1270340a30c5Ssensoray-dev sp->parm.capture.readbuffers = S2255_MIN_BUFS; 12715e950fafSDean Anderson def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000; 12725e950fafSDean Anderson def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000; 12730aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator = def_dem; 12745e950fafSDean Anderson switch (vc->mode.fdec) { 12750aa77f6cSMauro Carvalho Chehab default: 12760aa77f6cSMauro Carvalho Chehab case FDEC_1: 12770aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num; 12780aa77f6cSMauro Carvalho Chehab break; 12790aa77f6cSMauro Carvalho Chehab case FDEC_2: 12800aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 2; 12810aa77f6cSMauro Carvalho Chehab break; 12820aa77f6cSMauro Carvalho Chehab case FDEC_3: 12830aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 3; 12840aa77f6cSMauro Carvalho Chehab break; 12850aa77f6cSMauro Carvalho Chehab case FDEC_5: 12860aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 5; 12870aa77f6cSMauro Carvalho Chehab break; 12880aa77f6cSMauro Carvalho Chehab } 128992cde477SDean Anderson dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n", 1290f5402007Ssensoray-dev __func__, 12910aa77f6cSMauro Carvalho Chehab sp->parm.capture.capturemode, 12920aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator, 12930aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator); 12940aa77f6cSMauro Carvalho Chehab return 0; 12950aa77f6cSMauro Carvalho Chehab } 12960aa77f6cSMauro Carvalho Chehab 12970aa77f6cSMauro Carvalho Chehab static int vidioc_s_parm(struct file *file, void *priv, 12980aa77f6cSMauro Carvalho Chehab struct v4l2_streamparm *sp) 12990aa77f6cSMauro Carvalho Chehab { 1300340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 13010aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 13020aa77f6cSMauro Carvalho Chehab int fdec = FDEC_1; 13030aa77f6cSMauro Carvalho Chehab __u32 def_num, def_dem; 13040aa77f6cSMauro Carvalho Chehab if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 13050aa77f6cSMauro Carvalho Chehab return -EINVAL; 13065e950fafSDean Anderson mode = vc->mode; 13070aa77f6cSMauro Carvalho Chehab /* high quality capture mode requires a stream restart */ 1308340a30c5Ssensoray-dev if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode) 1309340a30c5Ssensoray-dev && vb2_is_streaming(&vc->vb_vidq)) 13100aa77f6cSMauro Carvalho Chehab return -EBUSY; 13110aa77f6cSMauro Carvalho Chehab def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; 13120aa77f6cSMauro Carvalho Chehab def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; 13130aa77f6cSMauro Carvalho Chehab if (def_dem != sp->parm.capture.timeperframe.denominator) 13140aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num; 13150aa77f6cSMauro Carvalho Chehab else if (sp->parm.capture.timeperframe.numerator <= def_num) 13160aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num; 13170aa77f6cSMauro Carvalho Chehab else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { 13180aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 2; 13190aa77f6cSMauro Carvalho Chehab fdec = FDEC_2; 13200aa77f6cSMauro Carvalho Chehab } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { 13210aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 3; 13220aa77f6cSMauro Carvalho Chehab fdec = FDEC_3; 13230aa77f6cSMauro Carvalho Chehab } else { 13240aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 5; 13250aa77f6cSMauro Carvalho Chehab fdec = FDEC_5; 13260aa77f6cSMauro Carvalho Chehab } 13270aa77f6cSMauro Carvalho Chehab mode.fdec = fdec; 13280aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator = def_dem; 1329340a30c5Ssensoray-dev sp->parm.capture.readbuffers = S2255_MIN_BUFS; 13305e950fafSDean Anderson s2255_set_mode(vc, &mode); 133192cde477SDean Anderson dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", 13320aa77f6cSMauro Carvalho Chehab __func__, 13330aa77f6cSMauro Carvalho Chehab sp->parm.capture.capturemode, 13340aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator, 13350aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator, fdec); 13360aa77f6cSMauro Carvalho Chehab return 0; 13370aa77f6cSMauro Carvalho Chehab } 13380aa77f6cSMauro Carvalho Chehab 133905e5d44bSHans Verkuil #define NUM_SIZE_ENUMS 3 134005e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete ntsc_sizes[] = { 134105e5d44bSHans Verkuil { 640, 480 }, 134205e5d44bSHans Verkuil { 640, 240 }, 134305e5d44bSHans Verkuil { 320, 240 }, 134405e5d44bSHans Verkuil }; 134505e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete pal_sizes[] = { 134605e5d44bSHans Verkuil { 704, 576 }, 134705e5d44bSHans Verkuil { 704, 288 }, 134805e5d44bSHans Verkuil { 352, 288 }, 134905e5d44bSHans Verkuil }; 135005e5d44bSHans Verkuil 135105e5d44bSHans Verkuil static int vidioc_enum_framesizes(struct file *file, void *priv, 135205e5d44bSHans Verkuil struct v4l2_frmsizeenum *fe) 135305e5d44bSHans Verkuil { 1354340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 13555e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 135605e5d44bSHans Verkuil const struct s2255_fmt *fmt; 135705e5d44bSHans Verkuil 135805e5d44bSHans Verkuil if (fe->index >= NUM_SIZE_ENUMS) 135905e5d44bSHans Verkuil return -EINVAL; 136005e5d44bSHans Verkuil 136105e5d44bSHans Verkuil fmt = format_by_fourcc(fe->pixel_format); 136205e5d44bSHans Verkuil if (fmt == NULL) 136305e5d44bSHans Verkuil return -EINVAL; 136405e5d44bSHans Verkuil fe->type = V4L2_FRMSIZE_TYPE_DISCRETE; 136505e5d44bSHans Verkuil fe->discrete = is_ntsc ? ntsc_sizes[fe->index] : pal_sizes[fe->index]; 136605e5d44bSHans Verkuil return 0; 136705e5d44bSHans Verkuil } 136805e5d44bSHans Verkuil 13690aa77f6cSMauro Carvalho Chehab static int vidioc_enum_frameintervals(struct file *file, void *priv, 13700aa77f6cSMauro Carvalho Chehab struct v4l2_frmivalenum *fe) 13710aa77f6cSMauro Carvalho Chehab { 1372340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 137305e5d44bSHans Verkuil const struct s2255_fmt *fmt; 137405e5d44bSHans Verkuil const struct v4l2_frmsize_discrete *sizes; 13755e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 13760aa77f6cSMauro Carvalho Chehab #define NUM_FRAME_ENUMS 4 13770aa77f6cSMauro Carvalho Chehab int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; 137805e5d44bSHans Verkuil int i; 137905e5d44bSHans Verkuil 1380199ab8feSMauro Carvalho Chehab if (fe->index >= NUM_FRAME_ENUMS) 13810aa77f6cSMauro Carvalho Chehab return -EINVAL; 138205e5d44bSHans Verkuil 138305e5d44bSHans Verkuil fmt = format_by_fourcc(fe->pixel_format); 138405e5d44bSHans Verkuil if (fmt == NULL) 13850aa77f6cSMauro Carvalho Chehab return -EINVAL; 138605e5d44bSHans Verkuil 138705e5d44bSHans Verkuil sizes = is_ntsc ? ntsc_sizes : pal_sizes; 138805e5d44bSHans Verkuil for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++) 138905e5d44bSHans Verkuil if (fe->width == sizes->width && 139005e5d44bSHans Verkuil fe->height == sizes->height) 13910aa77f6cSMauro Carvalho Chehab break; 139205e5d44bSHans Verkuil if (i == NUM_SIZE_ENUMS) 13930aa77f6cSMauro Carvalho Chehab return -EINVAL; 139405e5d44bSHans Verkuil 13950aa77f6cSMauro Carvalho Chehab fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; 13960aa77f6cSMauro Carvalho Chehab fe->discrete.denominator = is_ntsc ? 30000 : 25000; 13970aa77f6cSMauro Carvalho Chehab fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; 139892cde477SDean Anderson dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__, 1399f5402007Ssensoray-dev fe->discrete.numerator, 14000aa77f6cSMauro Carvalho Chehab fe->discrete.denominator); 14010aa77f6cSMauro Carvalho Chehab return 0; 14020aa77f6cSMauro Carvalho Chehab } 14030aa77f6cSMauro Carvalho Chehab 1404340a30c5Ssensoray-dev static int s2255_open(struct file *file) 14050aa77f6cSMauro Carvalho Chehab { 14065e950fafSDean Anderson struct s2255_vc *vc = video_drvdata(file); 1407340a30c5Ssensoray-dev struct s2255_dev *dev = vc->dev; 14080aa77f6cSMauro Carvalho Chehab int state; 1409340a30c5Ssensoray-dev int rc = 0; 1410340a30c5Ssensoray-dev 1411340a30c5Ssensoray-dev rc = v4l2_fh_open(file); 1412340a30c5Ssensoray-dev if (rc != 0) 1413340a30c5Ssensoray-dev return rc; 1414340a30c5Ssensoray-dev 1415340a30c5Ssensoray-dev dprintk(dev, 1, "s2255: %s\n", __func__); 14160aa77f6cSMauro Carvalho Chehab state = atomic_read(&dev->fw_data->fw_state); 14170aa77f6cSMauro Carvalho Chehab switch (state) { 14180aa77f6cSMauro Carvalho Chehab case S2255_FW_DISCONNECTING: 14190aa77f6cSMauro Carvalho Chehab return -ENODEV; 14200aa77f6cSMauro Carvalho Chehab case S2255_FW_FAILED: 14210aa77f6cSMauro Carvalho Chehab s2255_dev_err(&dev->udev->dev, 14220aa77f6cSMauro Carvalho Chehab "firmware load failed. retrying.\n"); 1423e2a06704SDean A s2255_fwload_start(dev); 14240aa77f6cSMauro Carvalho Chehab wait_event_timeout(dev->fw_data->wait_fw, 14250aa77f6cSMauro Carvalho Chehab ((atomic_read(&dev->fw_data->fw_state) 14260aa77f6cSMauro Carvalho Chehab == S2255_FW_SUCCESS) || 14270aa77f6cSMauro Carvalho Chehab (atomic_read(&dev->fw_data->fw_state) 14280aa77f6cSMauro Carvalho Chehab == S2255_FW_DISCONNECTING)), 14290aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_LOAD_TIMEOUT)); 14300aa77f6cSMauro Carvalho Chehab /* state may have changed, re-read */ 14310aa77f6cSMauro Carvalho Chehab state = atomic_read(&dev->fw_data->fw_state); 14320aa77f6cSMauro Carvalho Chehab break; 14330aa77f6cSMauro Carvalho Chehab case S2255_FW_NOTLOADED: 14340aa77f6cSMauro Carvalho Chehab case S2255_FW_LOADED_DSPWAIT: 14350aa77f6cSMauro Carvalho Chehab /* give S2255_LOAD_TIMEOUT time for firmware to load in case 14360aa77f6cSMauro Carvalho Chehab driver loaded and then device immediately opened */ 1437f5402007Ssensoray-dev pr_info("%s waiting for firmware load\n", __func__); 14380aa77f6cSMauro Carvalho Chehab wait_event_timeout(dev->fw_data->wait_fw, 14390aa77f6cSMauro Carvalho Chehab ((atomic_read(&dev->fw_data->fw_state) 14400aa77f6cSMauro Carvalho Chehab == S2255_FW_SUCCESS) || 14410aa77f6cSMauro Carvalho Chehab (atomic_read(&dev->fw_data->fw_state) 14420aa77f6cSMauro Carvalho Chehab == S2255_FW_DISCONNECTING)), 14430aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_LOAD_TIMEOUT)); 14440aa77f6cSMauro Carvalho Chehab /* state may have changed, re-read */ 14450aa77f6cSMauro Carvalho Chehab state = atomic_read(&dev->fw_data->fw_state); 14460aa77f6cSMauro Carvalho Chehab break; 14470aa77f6cSMauro Carvalho Chehab case S2255_FW_SUCCESS: 14480aa77f6cSMauro Carvalho Chehab default: 14490aa77f6cSMauro Carvalho Chehab break; 14500aa77f6cSMauro Carvalho Chehab } 14510aa77f6cSMauro Carvalho Chehab /* state may have changed in above switch statement */ 14520aa77f6cSMauro Carvalho Chehab switch (state) { 14530aa77f6cSMauro Carvalho Chehab case S2255_FW_SUCCESS: 14540aa77f6cSMauro Carvalho Chehab break; 14550aa77f6cSMauro Carvalho Chehab case S2255_FW_FAILED: 1456f5402007Ssensoray-dev pr_info("2255 firmware load failed.\n"); 14570aa77f6cSMauro Carvalho Chehab return -ENODEV; 14580aa77f6cSMauro Carvalho Chehab case S2255_FW_DISCONNECTING: 1459f5402007Ssensoray-dev pr_info("%s: disconnecting\n", __func__); 14600aa77f6cSMauro Carvalho Chehab return -ENODEV; 14610aa77f6cSMauro Carvalho Chehab case S2255_FW_LOADED_DSPWAIT: 14620aa77f6cSMauro Carvalho Chehab case S2255_FW_NOTLOADED: 1463f5402007Ssensoray-dev pr_info("%s: firmware not loaded, please retry\n", 14640aa77f6cSMauro Carvalho Chehab __func__); 14650aa77f6cSMauro Carvalho Chehab /* 14660aa77f6cSMauro Carvalho Chehab * Timeout on firmware load means device unusable. 14670aa77f6cSMauro Carvalho Chehab * Set firmware failure state. 14680aa77f6cSMauro Carvalho Chehab * On next s2255_open the firmware will be reloaded. 14690aa77f6cSMauro Carvalho Chehab */ 14700aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, 14710aa77f6cSMauro Carvalho Chehab S2255_FW_FAILED); 14720aa77f6cSMauro Carvalho Chehab return -EAGAIN; 14730aa77f6cSMauro Carvalho Chehab default: 1474f5402007Ssensoray-dev pr_info("%s: unknown state\n", __func__); 14750aa77f6cSMauro Carvalho Chehab return -EFAULT; 14760aa77f6cSMauro Carvalho Chehab } 14775e950fafSDean Anderson if (!vc->configured) { 14780aa77f6cSMauro Carvalho Chehab /* configure channel to default state */ 14795e950fafSDean Anderson vc->fmt = &formats[0]; 14805e950fafSDean Anderson s2255_set_mode(vc, &vc->mode); 14815e950fafSDean Anderson vc->configured = 1; 14820aa77f6cSMauro Carvalho Chehab } 14830aa77f6cSMauro Carvalho Chehab return 0; 14840aa77f6cSMauro Carvalho Chehab } 14850aa77f6cSMauro Carvalho Chehab 14860aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev) 14870aa77f6cSMauro Carvalho Chehab { 1488f5402007Ssensoray-dev dprintk(dev, 1, "%s", __func__); 14890aa77f6cSMauro Carvalho Chehab /* board shutdown stops the read pipe if it is running */ 14900aa77f6cSMauro Carvalho Chehab s2255_board_shutdown(dev); 14910aa77f6cSMauro Carvalho Chehab /* make sure firmware still not trying to load */ 14929f6be2bcSKirill Tkhai del_timer_sync(&dev->timer); /* only started in .probe and .open */ 14930aa77f6cSMauro Carvalho Chehab if (dev->fw_data->fw_urb) { 14940aa77f6cSMauro Carvalho Chehab usb_kill_urb(dev->fw_data->fw_urb); 14950aa77f6cSMauro Carvalho Chehab usb_free_urb(dev->fw_data->fw_urb); 14960aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_urb = NULL; 14970aa77f6cSMauro Carvalho Chehab } 14980aa77f6cSMauro Carvalho Chehab release_firmware(dev->fw_data->fw); 14990aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data->pfw_data); 15000aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data); 15010aa77f6cSMauro Carvalho Chehab /* reset the DSP so firmware can be reloaded next time */ 15020aa77f6cSMauro Carvalho Chehab s2255_reset_dsppower(dev); 15030aa77f6cSMauro Carvalho Chehab mutex_destroy(&dev->lock); 15040aa77f6cSMauro Carvalho Chehab usb_put_dev(dev->udev); 15050aa77f6cSMauro Carvalho Chehab v4l2_device_unregister(&dev->v4l2_dev); 150647d8c881SDean Anderson kfree(dev->cmdbuf); 15070aa77f6cSMauro Carvalho Chehab kfree(dev); 15080aa77f6cSMauro Carvalho Chehab } 15090aa77f6cSMauro Carvalho Chehab 15100aa77f6cSMauro Carvalho Chehab static const struct v4l2_file_operations s2255_fops_v4l = { 15110aa77f6cSMauro Carvalho Chehab .owner = THIS_MODULE, 15120aa77f6cSMauro Carvalho Chehab .open = s2255_open, 1513340a30c5Ssensoray-dev .release = vb2_fop_release, 1514340a30c5Ssensoray-dev .poll = vb2_fop_poll, 15150aa77f6cSMauro Carvalho Chehab .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ 1516340a30c5Ssensoray-dev .mmap = vb2_fop_mmap, 1517340a30c5Ssensoray-dev .read = vb2_fop_read, 15180aa77f6cSMauro Carvalho Chehab }; 15190aa77f6cSMauro Carvalho Chehab 15200aa77f6cSMauro Carvalho Chehab static const struct v4l2_ioctl_ops s2255_ioctl_ops = { 15210aa77f6cSMauro Carvalho Chehab .vidioc_querycap = vidioc_querycap, 15220aa77f6cSMauro Carvalho Chehab .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 15230aa77f6cSMauro Carvalho Chehab .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 15240aa77f6cSMauro Carvalho Chehab .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 15250aa77f6cSMauro Carvalho Chehab .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 1526340a30c5Ssensoray-dev .vidioc_reqbufs = vb2_ioctl_reqbufs, 1527340a30c5Ssensoray-dev .vidioc_querybuf = vb2_ioctl_querybuf, 1528340a30c5Ssensoray-dev .vidioc_qbuf = vb2_ioctl_qbuf, 1529340a30c5Ssensoray-dev .vidioc_dqbuf = vb2_ioctl_dqbuf, 15300aa77f6cSMauro Carvalho Chehab .vidioc_s_std = vidioc_s_std, 1531469af77aSHans Verkuil .vidioc_g_std = vidioc_g_std, 15320aa77f6cSMauro Carvalho Chehab .vidioc_enum_input = vidioc_enum_input, 15330aa77f6cSMauro Carvalho Chehab .vidioc_g_input = vidioc_g_input, 15340aa77f6cSMauro Carvalho Chehab .vidioc_s_input = vidioc_s_input, 1535340a30c5Ssensoray-dev .vidioc_streamon = vb2_ioctl_streamon, 1536340a30c5Ssensoray-dev .vidioc_streamoff = vb2_ioctl_streamoff, 15370aa77f6cSMauro Carvalho Chehab .vidioc_s_jpegcomp = vidioc_s_jpegcomp, 15380aa77f6cSMauro Carvalho Chehab .vidioc_g_jpegcomp = vidioc_g_jpegcomp, 15390aa77f6cSMauro Carvalho Chehab .vidioc_s_parm = vidioc_s_parm, 15400aa77f6cSMauro Carvalho Chehab .vidioc_g_parm = vidioc_g_parm, 154105e5d44bSHans Verkuil .vidioc_enum_framesizes = vidioc_enum_framesizes, 15420aa77f6cSMauro Carvalho Chehab .vidioc_enum_frameintervals = vidioc_enum_frameintervals, 154344d06d82SHans Verkuil .vidioc_log_status = v4l2_ctrl_log_status, 154444d06d82SHans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 154544d06d82SHans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 15460aa77f6cSMauro Carvalho Chehab }; 15470aa77f6cSMauro Carvalho Chehab 15480aa77f6cSMauro Carvalho Chehab static void s2255_video_device_release(struct video_device *vdev) 15490aa77f6cSMauro Carvalho Chehab { 15500aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); 15515e950fafSDean Anderson struct s2255_vc *vc = 15525e950fafSDean Anderson container_of(vdev, struct s2255_vc, vdev); 1553192f1e78SHans Verkuil 1554f5402007Ssensoray-dev dprintk(dev, 4, "%s, chnls: %d\n", __func__, 15550aa77f6cSMauro Carvalho Chehab atomic_read(&dev->num_channels)); 1556192f1e78SHans Verkuil 15575e950fafSDean Anderson v4l2_ctrl_handler_free(&vc->hdl); 1558f5402007Ssensoray-dev 15590aa77f6cSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->num_channels)) 15600aa77f6cSMauro Carvalho Chehab s2255_destroy(dev); 15610aa77f6cSMauro Carvalho Chehab return; 15620aa77f6cSMauro Carvalho Chehab } 15630aa77f6cSMauro Carvalho Chehab 156486844942SBhumika Goyal static const struct video_device template = { 15650aa77f6cSMauro Carvalho Chehab .name = "s2255v", 15660aa77f6cSMauro Carvalho Chehab .fops = &s2255_fops_v4l, 15670aa77f6cSMauro Carvalho Chehab .ioctl_ops = &s2255_ioctl_ops, 15680aa77f6cSMauro Carvalho Chehab .release = s2255_video_device_release, 15690aa77f6cSMauro Carvalho Chehab .tvnorms = S2255_NORMS, 15700aa77f6cSMauro Carvalho Chehab }; 15710aa77f6cSMauro Carvalho Chehab 1572192f1e78SHans Verkuil static const struct v4l2_ctrl_ops s2255_ctrl_ops = { 1573192f1e78SHans Verkuil .s_ctrl = s2255_s_ctrl, 1574192f1e78SHans Verkuil }; 1575192f1e78SHans Verkuil 1576192f1e78SHans Verkuil static const struct v4l2_ctrl_config color_filter_ctrl = { 1577192f1e78SHans Verkuil .ops = &s2255_ctrl_ops, 1578192f1e78SHans Verkuil .name = "Color Filter", 1579192f1e78SHans Verkuil .id = V4L2_CID_S2255_COLORFILTER, 1580192f1e78SHans Verkuil .type = V4L2_CTRL_TYPE_BOOLEAN, 1581192f1e78SHans Verkuil .max = 1, 1582192f1e78SHans Verkuil .step = 1, 1583192f1e78SHans Verkuil .def = 1, 1584192f1e78SHans Verkuil }; 1585192f1e78SHans Verkuil 15860aa77f6cSMauro Carvalho Chehab static int s2255_probe_v4l(struct s2255_dev *dev) 15870aa77f6cSMauro Carvalho Chehab { 15880aa77f6cSMauro Carvalho Chehab int ret; 15890aa77f6cSMauro Carvalho Chehab int i; 15900aa77f6cSMauro Carvalho Chehab int cur_nr = video_nr; 15915e950fafSDean Anderson struct s2255_vc *vc; 1592340a30c5Ssensoray-dev struct vb2_queue *q; 1593340a30c5Ssensoray-dev 15940aa77f6cSMauro Carvalho Chehab ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); 15950aa77f6cSMauro Carvalho Chehab if (ret) 15960aa77f6cSMauro Carvalho Chehab return ret; 15970aa77f6cSMauro Carvalho Chehab /* initialize all video 4 linux */ 15980aa77f6cSMauro Carvalho Chehab /* register 4 video devices */ 15990aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 16005e950fafSDean Anderson vc = &dev->vc[i]; 16015e950fafSDean Anderson INIT_LIST_HEAD(&vc->buf_list); 1602192f1e78SHans Verkuil 16035e950fafSDean Anderson v4l2_ctrl_handler_init(&vc->hdl, 6); 16045e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1605192f1e78SHans Verkuil V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT); 16065e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1607192f1e78SHans Verkuil V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST); 16085e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1609192f1e78SHans Verkuil V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION); 16105e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1611192f1e78SHans Verkuil V4L2_CID_HUE, 0, 255, 1, DEF_HUE); 16125e950fafSDean Anderson vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl, 16137041dec7SHans Verkuil &s2255_ctrl_ops, 16147041dec7SHans Verkuil V4L2_CID_JPEG_COMPRESSION_QUALITY, 16157041dec7SHans Verkuil 0, 100, 1, S2255_DEF_JPEG_QUAL); 1616192f1e78SHans Verkuil if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER && 16175e950fafSDean Anderson (dev->pid != 0x2257 || vc->idx <= 1)) 16185e950fafSDean Anderson v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl, 1619f5402007Ssensoray-dev NULL); 16205e950fafSDean Anderson if (vc->hdl.error) { 16215e950fafSDean Anderson ret = vc->hdl.error; 16225e950fafSDean Anderson v4l2_ctrl_handler_free(&vc->hdl); 1623192f1e78SHans Verkuil dev_err(&dev->udev->dev, "couldn't register control\n"); 1624192f1e78SHans Verkuil break; 1625192f1e78SHans Verkuil } 1626340a30c5Ssensoray-dev q = &vc->vb_vidq; 1627340a30c5Ssensoray-dev q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1628340a30c5Ssensoray-dev q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR; 1629340a30c5Ssensoray-dev q->drv_priv = vc; 1630340a30c5Ssensoray-dev q->lock = &vc->vb_lock; 1631340a30c5Ssensoray-dev q->buf_struct_size = sizeof(struct s2255_buffer); 1632340a30c5Ssensoray-dev q->mem_ops = &vb2_vmalloc_memops; 1633340a30c5Ssensoray-dev q->ops = &s2255_video_qops; 1634ade48681SSakari Ailus q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 1635340a30c5Ssensoray-dev ret = vb2_queue_init(q); 1636340a30c5Ssensoray-dev if (ret != 0) { 1637340a30c5Ssensoray-dev dev_err(&dev->udev->dev, 1638340a30c5Ssensoray-dev "%s vb2_queue_init 0x%x\n", __func__, ret); 1639340a30c5Ssensoray-dev break; 1640340a30c5Ssensoray-dev } 1641340a30c5Ssensoray-dev /* register video devices */ 16425e950fafSDean Anderson vc->vdev = template; 1643340a30c5Ssensoray-dev vc->vdev.queue = q; 16445e950fafSDean Anderson vc->vdev.ctrl_handler = &vc->hdl; 16455e950fafSDean Anderson vc->vdev.lock = &dev->lock; 16465e950fafSDean Anderson vc->vdev.v4l2_dev = &dev->v4l2_dev; 16478c3854d0SHans Verkuil vc->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | 16488c3854d0SHans Verkuil V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; 16495e950fafSDean Anderson video_set_drvdata(&vc->vdev, vc); 16500aa77f6cSMauro Carvalho Chehab if (video_nr == -1) 16515e950fafSDean Anderson ret = video_register_device(&vc->vdev, 1652*7fbbbc78SHans Verkuil VFL_TYPE_VIDEO, 16530aa77f6cSMauro Carvalho Chehab video_nr); 16540aa77f6cSMauro Carvalho Chehab else 16555e950fafSDean Anderson ret = video_register_device(&vc->vdev, 1656*7fbbbc78SHans Verkuil VFL_TYPE_VIDEO, 16570aa77f6cSMauro Carvalho Chehab cur_nr + i); 16580aa77f6cSMauro Carvalho Chehab 16590aa77f6cSMauro Carvalho Chehab if (ret) { 16600aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, 16610aa77f6cSMauro Carvalho Chehab "failed to register video device!\n"); 16620aa77f6cSMauro Carvalho Chehab break; 16630aa77f6cSMauro Carvalho Chehab } 16640aa77f6cSMauro Carvalho Chehab atomic_inc(&dev->num_channels); 16650aa77f6cSMauro Carvalho Chehab v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", 16665e950fafSDean Anderson video_device_node_name(&vc->vdev)); 16670aa77f6cSMauro Carvalho Chehab 16680aa77f6cSMauro Carvalho Chehab } 1669f5402007Ssensoray-dev pr_info("Sensoray 2255 V4L driver Revision: %s\n", 16700aa77f6cSMauro Carvalho Chehab S2255_VERSION); 16710aa77f6cSMauro Carvalho Chehab /* if no channels registered, return error and probe will fail*/ 16720aa77f6cSMauro Carvalho Chehab if (atomic_read(&dev->num_channels) == 0) { 16730aa77f6cSMauro Carvalho Chehab v4l2_device_unregister(&dev->v4l2_dev); 16740aa77f6cSMauro Carvalho Chehab return ret; 16750aa77f6cSMauro Carvalho Chehab } 16760aa77f6cSMauro Carvalho Chehab if (atomic_read(&dev->num_channels) != MAX_CHANNELS) 1677f5402007Ssensoray-dev pr_warn("s2255: Not all channels available.\n"); 16780aa77f6cSMauro Carvalho Chehab return 0; 16790aa77f6cSMauro Carvalho Chehab } 16800aa77f6cSMauro Carvalho Chehab 16810aa77f6cSMauro Carvalho Chehab /* this function moves the usb stream read pipe data 16820aa77f6cSMauro Carvalho Chehab * into the system buffers. 16830aa77f6cSMauro Carvalho Chehab * returns 0 on success, EAGAIN if more data to process( call this 16840aa77f6cSMauro Carvalho Chehab * function again). 16850aa77f6cSMauro Carvalho Chehab * 16860aa77f6cSMauro Carvalho Chehab * Received frame structure: 16870aa77f6cSMauro Carvalho Chehab * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) 16880aa77f6cSMauro Carvalho Chehab * bytes 4-7: channel: 0-3 16890aa77f6cSMauro Carvalho Chehab * bytes 8-11: payload size: size of the frame 16900aa77f6cSMauro Carvalho Chehab * bytes 12-payloadsize+12: frame data 16910aa77f6cSMauro Carvalho Chehab */ 16920aa77f6cSMauro Carvalho Chehab static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) 16930aa77f6cSMauro Carvalho Chehab { 16940aa77f6cSMauro Carvalho Chehab char *pdest; 16950aa77f6cSMauro Carvalho Chehab u32 offset = 0; 16960aa77f6cSMauro Carvalho Chehab int bframe = 0; 16970aa77f6cSMauro Carvalho Chehab char *psrc; 16980aa77f6cSMauro Carvalho Chehab unsigned long copy_size; 16990aa77f6cSMauro Carvalho Chehab unsigned long size; 17000aa77f6cSMauro Carvalho Chehab s32 idx = -1; 17010aa77f6cSMauro Carvalho Chehab struct s2255_framei *frm; 17020aa77f6cSMauro Carvalho Chehab unsigned char *pdata; 17035e950fafSDean Anderson struct s2255_vc *vc; 1704f5402007Ssensoray-dev dprintk(dev, 100, "buffer to user\n"); 17055e950fafSDean Anderson vc = &dev->vc[dev->cc]; 17065e950fafSDean Anderson idx = vc->cur_frame; 17075e950fafSDean Anderson frm = &vc->buffer.frame[idx]; 17080aa77f6cSMauro Carvalho Chehab if (frm->ulState == S2255_READ_IDLE) { 17090aa77f6cSMauro Carvalho Chehab int jj; 17100aa77f6cSMauro Carvalho Chehab unsigned int cc; 17110aa77f6cSMauro Carvalho Chehab __le32 *pdword; /*data from dsp is little endian */ 17120aa77f6cSMauro Carvalho Chehab int payload; 17130aa77f6cSMauro Carvalho Chehab /* search for marker codes */ 17140aa77f6cSMauro Carvalho Chehab pdata = (unsigned char *)pipe_info->transfer_buffer; 17150aa77f6cSMauro Carvalho Chehab pdword = (__le32 *)pdata; 17160aa77f6cSMauro Carvalho Chehab for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { 17170aa77f6cSMauro Carvalho Chehab switch (*pdword) { 17180aa77f6cSMauro Carvalho Chehab case S2255_MARKER_FRAME: 1719f5402007Ssensoray-dev dprintk(dev, 4, "marker @ offset: %d [%x %x]\n", 1720f5402007Ssensoray-dev jj, pdata[0], pdata[1]); 17210aa77f6cSMauro Carvalho Chehab offset = jj + PREFIX_SIZE; 17220aa77f6cSMauro Carvalho Chehab bframe = 1; 17230aa77f6cSMauro Carvalho Chehab cc = le32_to_cpu(pdword[1]); 17240aa77f6cSMauro Carvalho Chehab if (cc >= MAX_CHANNELS) { 1725f5402007Ssensoray-dev dprintk(dev, 0, 17260aa77f6cSMauro Carvalho Chehab "bad channel\n"); 17270aa77f6cSMauro Carvalho Chehab return -EINVAL; 17280aa77f6cSMauro Carvalho Chehab } 17290aa77f6cSMauro Carvalho Chehab /* reverse it */ 17300aa77f6cSMauro Carvalho Chehab dev->cc = G_chnmap[cc]; 17315e950fafSDean Anderson vc = &dev->vc[dev->cc]; 17320aa77f6cSMauro Carvalho Chehab payload = le32_to_cpu(pdword[3]); 17335e950fafSDean Anderson if (payload > vc->req_image_size) { 17345e950fafSDean Anderson vc->bad_payload++; 17350aa77f6cSMauro Carvalho Chehab /* discard the bad frame */ 17360aa77f6cSMauro Carvalho Chehab return -EINVAL; 17370aa77f6cSMauro Carvalho Chehab } 17385e950fafSDean Anderson vc->pkt_size = payload; 17395e950fafSDean Anderson vc->jpg_size = le32_to_cpu(pdword[4]); 17400aa77f6cSMauro Carvalho Chehab break; 17410aa77f6cSMauro Carvalho Chehab case S2255_MARKER_RESPONSE: 17420aa77f6cSMauro Carvalho Chehab 17430aa77f6cSMauro Carvalho Chehab pdata += DEF_USB_BLOCK; 17440aa77f6cSMauro Carvalho Chehab jj += DEF_USB_BLOCK; 17450aa77f6cSMauro Carvalho Chehab if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) 17460aa77f6cSMauro Carvalho Chehab break; 17470aa77f6cSMauro Carvalho Chehab cc = G_chnmap[le32_to_cpu(pdword[1])]; 17480aa77f6cSMauro Carvalho Chehab if (cc >= MAX_CHANNELS) 17490aa77f6cSMauro Carvalho Chehab break; 17505e950fafSDean Anderson vc = &dev->vc[cc]; 17510aa77f6cSMauro Carvalho Chehab switch (pdword[2]) { 17520aa77f6cSMauro Carvalho Chehab case S2255_RESPONSE_SETMODE: 17530aa77f6cSMauro Carvalho Chehab /* check if channel valid */ 17540aa77f6cSMauro Carvalho Chehab /* set mode ready */ 17555e950fafSDean Anderson vc->setmode_ready = 1; 17565e950fafSDean Anderson wake_up(&vc->wait_setmode); 1757f5402007Ssensoray-dev dprintk(dev, 5, "setmode rdy %d\n", cc); 17580aa77f6cSMauro Carvalho Chehab break; 17590aa77f6cSMauro Carvalho Chehab case S2255_RESPONSE_FW: 17600aa77f6cSMauro Carvalho Chehab dev->chn_ready |= (1 << cc); 17610aa77f6cSMauro Carvalho Chehab if ((dev->chn_ready & 0x0f) != 0x0f) 17620aa77f6cSMauro Carvalho Chehab break; 17630aa77f6cSMauro Carvalho Chehab /* all channels ready */ 1764f5402007Ssensoray-dev pr_info("s2255: fw loaded\n"); 17650aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, 17660aa77f6cSMauro Carvalho Chehab S2255_FW_SUCCESS); 17670aa77f6cSMauro Carvalho Chehab wake_up(&dev->fw_data->wait_fw); 17680aa77f6cSMauro Carvalho Chehab break; 17690aa77f6cSMauro Carvalho Chehab case S2255_RESPONSE_STATUS: 17705e950fafSDean Anderson vc->vidstatus = le32_to_cpu(pdword[3]); 17715e950fafSDean Anderson vc->vidstatus_ready = 1; 17725e950fafSDean Anderson wake_up(&vc->wait_vidstatus); 1773f5402007Ssensoray-dev dprintk(dev, 5, "vstat %x chan %d\n", 17740aa77f6cSMauro Carvalho Chehab le32_to_cpu(pdword[3]), cc); 17750aa77f6cSMauro Carvalho Chehab break; 17760aa77f6cSMauro Carvalho Chehab default: 1777f5402007Ssensoray-dev pr_info("s2255 unknown resp\n"); 17780aa77f6cSMauro Carvalho Chehab } 1779ec33fbd5SMauro Carvalho Chehab pdata++; 1780ec33fbd5SMauro Carvalho Chehab break; 17810aa77f6cSMauro Carvalho Chehab default: 17820aa77f6cSMauro Carvalho Chehab pdata++; 17830aa77f6cSMauro Carvalho Chehab break; 17840aa77f6cSMauro Carvalho Chehab } 17850aa77f6cSMauro Carvalho Chehab if (bframe) 17860aa77f6cSMauro Carvalho Chehab break; 17870aa77f6cSMauro Carvalho Chehab } /* for */ 17880aa77f6cSMauro Carvalho Chehab if (!bframe) 17890aa77f6cSMauro Carvalho Chehab return -EINVAL; 17900aa77f6cSMauro Carvalho Chehab } 17915e950fafSDean Anderson vc = &dev->vc[dev->cc]; 17925e950fafSDean Anderson idx = vc->cur_frame; 17935e950fafSDean Anderson frm = &vc->buffer.frame[idx]; 17940aa77f6cSMauro Carvalho Chehab /* search done. now find out if should be acquiring on this channel */ 1795340a30c5Ssensoray-dev if (!vb2_is_streaming(&vc->vb_vidq)) { 17960aa77f6cSMauro Carvalho Chehab /* we found a frame, but this channel is turned off */ 17970aa77f6cSMauro Carvalho Chehab frm->ulState = S2255_READ_IDLE; 17980aa77f6cSMauro Carvalho Chehab return -EINVAL; 17990aa77f6cSMauro Carvalho Chehab } 18000aa77f6cSMauro Carvalho Chehab 18010aa77f6cSMauro Carvalho Chehab if (frm->ulState == S2255_READ_IDLE) { 18020aa77f6cSMauro Carvalho Chehab frm->ulState = S2255_READ_FRAME; 18030aa77f6cSMauro Carvalho Chehab frm->cur_size = 0; 18040aa77f6cSMauro Carvalho Chehab } 18050aa77f6cSMauro Carvalho Chehab 18060aa77f6cSMauro Carvalho Chehab /* skip the marker 512 bytes (and offset if out of sync) */ 18070aa77f6cSMauro Carvalho Chehab psrc = (u8 *)pipe_info->transfer_buffer + offset; 18080aa77f6cSMauro Carvalho Chehab 18090aa77f6cSMauro Carvalho Chehab 18100aa77f6cSMauro Carvalho Chehab if (frm->lpvbits == NULL) { 1811f5402007Ssensoray-dev dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d", 18120aa77f6cSMauro Carvalho Chehab frm, dev, dev->cc, idx); 18130aa77f6cSMauro Carvalho Chehab return -ENOMEM; 18140aa77f6cSMauro Carvalho Chehab } 18150aa77f6cSMauro Carvalho Chehab 18160aa77f6cSMauro Carvalho Chehab pdest = frm->lpvbits + frm->cur_size; 18170aa77f6cSMauro Carvalho Chehab 18180aa77f6cSMauro Carvalho Chehab copy_size = (pipe_info->cur_transfer_size - offset); 18190aa77f6cSMauro Carvalho Chehab 18205e950fafSDean Anderson size = vc->pkt_size - PREFIX_SIZE; 18210aa77f6cSMauro Carvalho Chehab 18220aa77f6cSMauro Carvalho Chehab /* sanity check on pdest */ 18235e950fafSDean Anderson if ((copy_size + frm->cur_size) < vc->req_image_size) 18240aa77f6cSMauro Carvalho Chehab memcpy(pdest, psrc, copy_size); 18250aa77f6cSMauro Carvalho Chehab 18260aa77f6cSMauro Carvalho Chehab frm->cur_size += copy_size; 1827f5402007Ssensoray-dev dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size); 18280aa77f6cSMauro Carvalho Chehab 18290aa77f6cSMauro Carvalho Chehab if (frm->cur_size >= size) { 1830f5402007Ssensoray-dev dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n", 18310aa77f6cSMauro Carvalho Chehab dev->cc, idx); 18325e950fafSDean Anderson vc->last_frame = vc->cur_frame; 18335e950fafSDean Anderson vc->cur_frame++; 18340aa77f6cSMauro Carvalho Chehab /* end of system frame ring buffer, start at zero */ 18355e950fafSDean Anderson if ((vc->cur_frame == SYS_FRAMES) || 18365e950fafSDean Anderson (vc->cur_frame == vc->buffer.dwFrames)) 18375e950fafSDean Anderson vc->cur_frame = 0; 18380aa77f6cSMauro Carvalho Chehab /* frame ready */ 1839340a30c5Ssensoray-dev if (vb2_is_streaming(&vc->vb_vidq)) 18405e950fafSDean Anderson s2255_got_frame(vc, vc->jpg_size); 18415e950fafSDean Anderson vc->frame_count++; 18420aa77f6cSMauro Carvalho Chehab frm->ulState = S2255_READ_IDLE; 18430aa77f6cSMauro Carvalho Chehab frm->cur_size = 0; 18440aa77f6cSMauro Carvalho Chehab 18450aa77f6cSMauro Carvalho Chehab } 18460aa77f6cSMauro Carvalho Chehab /* done successfully */ 18470aa77f6cSMauro Carvalho Chehab return 0; 18480aa77f6cSMauro Carvalho Chehab } 18490aa77f6cSMauro Carvalho Chehab 18500aa77f6cSMauro Carvalho Chehab static void s2255_read_video_callback(struct s2255_dev *dev, 18510aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe_info) 18520aa77f6cSMauro Carvalho Chehab { 18530aa77f6cSMauro Carvalho Chehab int res; 1854f5402007Ssensoray-dev dprintk(dev, 50, "callback read video\n"); 18550aa77f6cSMauro Carvalho Chehab 18560aa77f6cSMauro Carvalho Chehab if (dev->cc >= MAX_CHANNELS) { 18570aa77f6cSMauro Carvalho Chehab dev->cc = 0; 18580aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "invalid channel\n"); 18590aa77f6cSMauro Carvalho Chehab return; 18600aa77f6cSMauro Carvalho Chehab } 18610aa77f6cSMauro Carvalho Chehab /* otherwise copy to the system buffers */ 18620aa77f6cSMauro Carvalho Chehab res = save_frame(dev, pipe_info); 18630aa77f6cSMauro Carvalho Chehab if (res != 0) 1864f5402007Ssensoray-dev dprintk(dev, 4, "s2255: read callback failed\n"); 18650aa77f6cSMauro Carvalho Chehab 1866f5402007Ssensoray-dev dprintk(dev, 50, "callback read video done\n"); 18670aa77f6cSMauro Carvalho Chehab return; 18680aa77f6cSMauro Carvalho Chehab } 18690aa77f6cSMauro Carvalho Chehab 18700aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, 18710aa77f6cSMauro Carvalho Chehab u16 Index, u16 Value, void *TransferBuffer, 18720aa77f6cSMauro Carvalho Chehab s32 TransferBufferLength, int bOut) 18730aa77f6cSMauro Carvalho Chehab { 18740aa77f6cSMauro Carvalho Chehab int r; 1875db65c49eSMauro Carvalho Chehab unsigned char *buf; 1876db65c49eSMauro Carvalho Chehab 1877db65c49eSMauro Carvalho Chehab buf = kmalloc(TransferBufferLength, GFP_KERNEL); 1878db65c49eSMauro Carvalho Chehab if (!buf) 1879db65c49eSMauro Carvalho Chehab return -ENOMEM; 1880db65c49eSMauro Carvalho Chehab 18810aa77f6cSMauro Carvalho Chehab if (!bOut) { 18820aa77f6cSMauro Carvalho Chehab r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), 18830aa77f6cSMauro Carvalho Chehab Request, 18840aa77f6cSMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | 18850aa77f6cSMauro Carvalho Chehab USB_DIR_IN, 1886db65c49eSMauro Carvalho Chehab Value, Index, buf, 18870aa77f6cSMauro Carvalho Chehab TransferBufferLength, HZ * 5); 1888db65c49eSMauro Carvalho Chehab 1889db65c49eSMauro Carvalho Chehab if (r >= 0) 1890db65c49eSMauro Carvalho Chehab memcpy(TransferBuffer, buf, TransferBufferLength); 18910aa77f6cSMauro Carvalho Chehab } else { 1892db65c49eSMauro Carvalho Chehab memcpy(buf, TransferBuffer, TransferBufferLength); 18930aa77f6cSMauro Carvalho Chehab r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), 18940aa77f6cSMauro Carvalho Chehab Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1895db65c49eSMauro Carvalho Chehab Value, Index, buf, 18960aa77f6cSMauro Carvalho Chehab TransferBufferLength, HZ * 5); 18970aa77f6cSMauro Carvalho Chehab } 1898db65c49eSMauro Carvalho Chehab kfree(buf); 18990aa77f6cSMauro Carvalho Chehab return r; 19000aa77f6cSMauro Carvalho Chehab } 19010aa77f6cSMauro Carvalho Chehab 19020aa77f6cSMauro Carvalho Chehab /* 19030aa77f6cSMauro Carvalho Chehab * retrieve FX2 firmware version. future use. 19040aa77f6cSMauro Carvalho Chehab * @param dev pointer to device extension 19050aa77f6cSMauro Carvalho Chehab * @return -1 for fail, else returns firmware version as an int(16 bits) 19060aa77f6cSMauro Carvalho Chehab */ 19070aa77f6cSMauro Carvalho Chehab static int s2255_get_fx2fw(struct s2255_dev *dev) 19080aa77f6cSMauro Carvalho Chehab { 19090aa77f6cSMauro Carvalho Chehab int fw; 19100aa77f6cSMauro Carvalho Chehab int ret; 19110aa77f6cSMauro Carvalho Chehab unsigned char transBuffer[64]; 19120aa77f6cSMauro Carvalho Chehab ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, 19130aa77f6cSMauro Carvalho Chehab S2255_VR_IN); 19140aa77f6cSMauro Carvalho Chehab if (ret < 0) 1915f5402007Ssensoray-dev dprintk(dev, 2, "get fw error: %x\n", ret); 19160aa77f6cSMauro Carvalho Chehab fw = transBuffer[0] + (transBuffer[1] << 8); 1917f5402007Ssensoray-dev dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); 19180aa77f6cSMauro Carvalho Chehab return fw; 19190aa77f6cSMauro Carvalho Chehab } 19200aa77f6cSMauro Carvalho Chehab 19210aa77f6cSMauro Carvalho Chehab /* 19220aa77f6cSMauro Carvalho Chehab * Create the system ring buffer to copy frames into from the 19230aa77f6cSMauro Carvalho Chehab * usb read pipe. 19240aa77f6cSMauro Carvalho Chehab */ 19255e950fafSDean Anderson static int s2255_create_sys_buffers(struct s2255_vc *vc) 19260aa77f6cSMauro Carvalho Chehab { 19270aa77f6cSMauro Carvalho Chehab unsigned long i; 19280aa77f6cSMauro Carvalho Chehab unsigned long reqsize; 19295e950fafSDean Anderson vc->buffer.dwFrames = SYS_FRAMES; 19300aa77f6cSMauro Carvalho Chehab /* always allocate maximum size(PAL) for system buffers */ 19310aa77f6cSMauro Carvalho Chehab reqsize = SYS_FRAMES_MAXSIZE; 19320aa77f6cSMauro Carvalho Chehab 19330aa77f6cSMauro Carvalho Chehab if (reqsize > SYS_FRAMES_MAXSIZE) 19340aa77f6cSMauro Carvalho Chehab reqsize = SYS_FRAMES_MAXSIZE; 19350aa77f6cSMauro Carvalho Chehab 19360aa77f6cSMauro Carvalho Chehab for (i = 0; i < SYS_FRAMES; i++) { 19370aa77f6cSMauro Carvalho Chehab /* allocate the frames */ 19385e950fafSDean Anderson vc->buffer.frame[i].lpvbits = vmalloc(reqsize); 19395e950fafSDean Anderson vc->buffer.frame[i].size = reqsize; 19405e950fafSDean Anderson if (vc->buffer.frame[i].lpvbits == NULL) { 1941f5402007Ssensoray-dev pr_info("out of memory. using less frames\n"); 19425e950fafSDean Anderson vc->buffer.dwFrames = i; 19430aa77f6cSMauro Carvalho Chehab break; 19440aa77f6cSMauro Carvalho Chehab } 19450aa77f6cSMauro Carvalho Chehab } 19460aa77f6cSMauro Carvalho Chehab 19470aa77f6cSMauro Carvalho Chehab /* make sure internal states are set */ 19480aa77f6cSMauro Carvalho Chehab for (i = 0; i < SYS_FRAMES; i++) { 19495e950fafSDean Anderson vc->buffer.frame[i].ulState = 0; 19505e950fafSDean Anderson vc->buffer.frame[i].cur_size = 0; 19510aa77f6cSMauro Carvalho Chehab } 19520aa77f6cSMauro Carvalho Chehab 19535e950fafSDean Anderson vc->cur_frame = 0; 19545e950fafSDean Anderson vc->last_frame = -1; 19550aa77f6cSMauro Carvalho Chehab return 0; 19560aa77f6cSMauro Carvalho Chehab } 19570aa77f6cSMauro Carvalho Chehab 19585e950fafSDean Anderson static int s2255_release_sys_buffers(struct s2255_vc *vc) 19590aa77f6cSMauro Carvalho Chehab { 19600aa77f6cSMauro Carvalho Chehab unsigned long i; 19610aa77f6cSMauro Carvalho Chehab for (i = 0; i < SYS_FRAMES; i++) { 19625e950fafSDean Anderson vfree(vc->buffer.frame[i].lpvbits); 19635e950fafSDean Anderson vc->buffer.frame[i].lpvbits = NULL; 19640aa77f6cSMauro Carvalho Chehab } 19650aa77f6cSMauro Carvalho Chehab return 0; 19660aa77f6cSMauro Carvalho Chehab } 19670aa77f6cSMauro Carvalho Chehab 19680aa77f6cSMauro Carvalho Chehab static int s2255_board_init(struct s2255_dev *dev) 19690aa77f6cSMauro Carvalho Chehab { 19700aa77f6cSMauro Carvalho Chehab struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; 19710aa77f6cSMauro Carvalho Chehab int fw_ver; 19720aa77f6cSMauro Carvalho Chehab int j; 19730aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe = &dev->pipe; 1974f5402007Ssensoray-dev dprintk(dev, 4, "board init: %p", dev); 19750aa77f6cSMauro Carvalho Chehab memset(pipe, 0, sizeof(*pipe)); 19760aa77f6cSMauro Carvalho Chehab pipe->dev = dev; 19770aa77f6cSMauro Carvalho Chehab pipe->cur_transfer_size = S2255_USB_XFER_SIZE; 19780aa77f6cSMauro Carvalho Chehab pipe->max_transfer_size = S2255_USB_XFER_SIZE; 19790aa77f6cSMauro Carvalho Chehab 19800aa77f6cSMauro Carvalho Chehab pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, 19810aa77f6cSMauro Carvalho Chehab GFP_KERNEL); 19820aa77f6cSMauro Carvalho Chehab if (pipe->transfer_buffer == NULL) { 1983f5402007Ssensoray-dev dprintk(dev, 1, "out of memory!\n"); 19840aa77f6cSMauro Carvalho Chehab return -ENOMEM; 19850aa77f6cSMauro Carvalho Chehab } 19860aa77f6cSMauro Carvalho Chehab /* query the firmware */ 19870aa77f6cSMauro Carvalho Chehab fw_ver = s2255_get_fx2fw(dev); 19880aa77f6cSMauro Carvalho Chehab 1989f5402007Ssensoray-dev pr_info("s2255: usb firmware version %d.%d\n", 19900aa77f6cSMauro Carvalho Chehab (fw_ver >> 8) & 0xff, 19910aa77f6cSMauro Carvalho Chehab fw_ver & 0xff); 19920aa77f6cSMauro Carvalho Chehab 19930aa77f6cSMauro Carvalho Chehab if (fw_ver < S2255_CUR_USB_FWVER) 1994f5402007Ssensoray-dev pr_info("s2255: newer USB firmware available\n"); 19950aa77f6cSMauro Carvalho Chehab 19960aa77f6cSMauro Carvalho Chehab for (j = 0; j < MAX_CHANNELS; j++) { 19975e950fafSDean Anderson struct s2255_vc *vc = &dev->vc[j]; 19985e950fafSDean Anderson vc->mode = mode_def; 19990aa77f6cSMauro Carvalho Chehab if (dev->pid == 0x2257 && j > 1) 20005e950fafSDean Anderson vc->mode.color |= (1 << 16); 20015e950fafSDean Anderson vc->jpegqual = S2255_DEF_JPEG_QUAL; 20025e950fafSDean Anderson vc->width = LINE_SZ_4CIFS_NTSC; 20035e950fafSDean Anderson vc->height = NUM_LINES_4CIFS_NTSC * 2; 20045e950fafSDean Anderson vc->std = V4L2_STD_NTSC_M; 20055e950fafSDean Anderson vc->fmt = &formats[0]; 20065e950fafSDean Anderson vc->mode.restart = 1; 20075e950fafSDean Anderson vc->req_image_size = get_transfer_size(&mode_def); 20085e950fafSDean Anderson vc->frame_count = 0; 20090aa77f6cSMauro Carvalho Chehab /* create the system buffers */ 20105e950fafSDean Anderson s2255_create_sys_buffers(vc); 20110aa77f6cSMauro Carvalho Chehab } 20120aa77f6cSMauro Carvalho Chehab /* start read pipe */ 20130aa77f6cSMauro Carvalho Chehab s2255_start_readpipe(dev); 2014f5402007Ssensoray-dev dprintk(dev, 1, "%s: success\n", __func__); 20150aa77f6cSMauro Carvalho Chehab return 0; 20160aa77f6cSMauro Carvalho Chehab } 20170aa77f6cSMauro Carvalho Chehab 20180aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev) 20190aa77f6cSMauro Carvalho Chehab { 20200aa77f6cSMauro Carvalho Chehab u32 i; 2021f5402007Ssensoray-dev dprintk(dev, 1, "%s: dev: %p", __func__, dev); 20220aa77f6cSMauro Carvalho Chehab 20230aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 2024340a30c5Ssensoray-dev if (vb2_is_streaming(&dev->vc[i].vb_vidq)) 20255e950fafSDean Anderson s2255_stop_acquire(&dev->vc[i]); 20260aa77f6cSMauro Carvalho Chehab } 20270aa77f6cSMauro Carvalho Chehab s2255_stop_readpipe(dev); 20280aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) 20295e950fafSDean Anderson s2255_release_sys_buffers(&dev->vc[i]); 20300aa77f6cSMauro Carvalho Chehab /* release transfer buffer */ 20310aa77f6cSMauro Carvalho Chehab kfree(dev->pipe.transfer_buffer); 20320aa77f6cSMauro Carvalho Chehab return 0; 20330aa77f6cSMauro Carvalho Chehab } 20340aa77f6cSMauro Carvalho Chehab 20350aa77f6cSMauro Carvalho Chehab static void read_pipe_completion(struct urb *purb) 20360aa77f6cSMauro Carvalho Chehab { 20370aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe_info; 20380aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev; 20390aa77f6cSMauro Carvalho Chehab int status; 20400aa77f6cSMauro Carvalho Chehab int pipe; 20410aa77f6cSMauro Carvalho Chehab pipe_info = purb->context; 20420aa77f6cSMauro Carvalho Chehab if (pipe_info == NULL) { 20430aa77f6cSMauro Carvalho Chehab dev_err(&purb->dev->dev, "no context!\n"); 20440aa77f6cSMauro Carvalho Chehab return; 20450aa77f6cSMauro Carvalho Chehab } 20460aa77f6cSMauro Carvalho Chehab dev = pipe_info->dev; 20470aa77f6cSMauro Carvalho Chehab if (dev == NULL) { 20480aa77f6cSMauro Carvalho Chehab dev_err(&purb->dev->dev, "no context!\n"); 20490aa77f6cSMauro Carvalho Chehab return; 20500aa77f6cSMauro Carvalho Chehab } 20510aa77f6cSMauro Carvalho Chehab status = purb->status; 20520aa77f6cSMauro Carvalho Chehab /* if shutting down, do not resubmit, exit immediately */ 20530aa77f6cSMauro Carvalho Chehab if (status == -ESHUTDOWN) { 2054f5402007Ssensoray-dev dprintk(dev, 2, "%s: err shutdown\n", __func__); 20550aa77f6cSMauro Carvalho Chehab pipe_info->err_count++; 20560aa77f6cSMauro Carvalho Chehab return; 20570aa77f6cSMauro Carvalho Chehab } 20580aa77f6cSMauro Carvalho Chehab 20590aa77f6cSMauro Carvalho Chehab if (pipe_info->state == 0) { 2060f5402007Ssensoray-dev dprintk(dev, 2, "%s: exiting USB pipe", __func__); 20610aa77f6cSMauro Carvalho Chehab return; 20620aa77f6cSMauro Carvalho Chehab } 20630aa77f6cSMauro Carvalho Chehab 20640aa77f6cSMauro Carvalho Chehab if (status == 0) 20650aa77f6cSMauro Carvalho Chehab s2255_read_video_callback(dev, pipe_info); 20660aa77f6cSMauro Carvalho Chehab else { 20670aa77f6cSMauro Carvalho Chehab pipe_info->err_count++; 2068f5402007Ssensoray-dev dprintk(dev, 1, "%s: failed URB %d\n", __func__, status); 20690aa77f6cSMauro Carvalho Chehab } 20700aa77f6cSMauro Carvalho Chehab 20710aa77f6cSMauro Carvalho Chehab pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); 20720aa77f6cSMauro Carvalho Chehab /* reuse urb */ 20730aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, 20740aa77f6cSMauro Carvalho Chehab pipe, 20750aa77f6cSMauro Carvalho Chehab pipe_info->transfer_buffer, 20760aa77f6cSMauro Carvalho Chehab pipe_info->cur_transfer_size, 20770aa77f6cSMauro Carvalho Chehab read_pipe_completion, pipe_info); 20780aa77f6cSMauro Carvalho Chehab 20790aa77f6cSMauro Carvalho Chehab if (pipe_info->state != 0) { 2080f5402007Ssensoray-dev if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) 20810aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "error submitting urb\n"); 20820aa77f6cSMauro Carvalho Chehab } else { 2083f5402007Ssensoray-dev dprintk(dev, 2, "%s :complete state 0\n", __func__); 20840aa77f6cSMauro Carvalho Chehab } 20850aa77f6cSMauro Carvalho Chehab return; 20860aa77f6cSMauro Carvalho Chehab } 20870aa77f6cSMauro Carvalho Chehab 20880aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev) 20890aa77f6cSMauro Carvalho Chehab { 20900aa77f6cSMauro Carvalho Chehab int pipe; 20910aa77f6cSMauro Carvalho Chehab int retval; 20920aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe_info = &dev->pipe; 20930aa77f6cSMauro Carvalho Chehab pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); 2094f5402007Ssensoray-dev dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint); 20950aa77f6cSMauro Carvalho Chehab pipe_info->state = 1; 20960aa77f6cSMauro Carvalho Chehab pipe_info->err_count = 0; 20970aa77f6cSMauro Carvalho Chehab pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); 2098fc56da79SWolfram Sang if (!pipe_info->stream_urb) 20990aa77f6cSMauro Carvalho Chehab return -ENOMEM; 21000aa77f6cSMauro Carvalho Chehab /* transfer buffer allocated in board_init */ 21010aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, 21020aa77f6cSMauro Carvalho Chehab pipe, 21030aa77f6cSMauro Carvalho Chehab pipe_info->transfer_buffer, 21040aa77f6cSMauro Carvalho Chehab pipe_info->cur_transfer_size, 21050aa77f6cSMauro Carvalho Chehab read_pipe_completion, pipe_info); 21060aa77f6cSMauro Carvalho Chehab retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); 21070aa77f6cSMauro Carvalho Chehab if (retval) { 2108f5402007Ssensoray-dev pr_err("s2255: start read pipe failed\n"); 21090aa77f6cSMauro Carvalho Chehab return retval; 21100aa77f6cSMauro Carvalho Chehab } 21110aa77f6cSMauro Carvalho Chehab return 0; 21120aa77f6cSMauro Carvalho Chehab } 21130aa77f6cSMauro Carvalho Chehab 21140aa77f6cSMauro Carvalho Chehab /* starts acquisition process */ 21155e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc) 21160aa77f6cSMauro Carvalho Chehab { 21170aa77f6cSMauro Carvalho Chehab int res; 21180aa77f6cSMauro Carvalho Chehab unsigned long chn_rev; 21190aa77f6cSMauro Carvalho Chehab int j; 21205e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 212147d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 21220aa77f6cSMauro Carvalho Chehab 212347d8c881SDean Anderson mutex_lock(&dev->cmdlock); 212447d8c881SDean Anderson chn_rev = G_chnmap[vc->idx]; 21255e950fafSDean Anderson vc->last_frame = -1; 21265e950fafSDean Anderson vc->bad_payload = 0; 21275e950fafSDean Anderson vc->cur_frame = 0; 21280aa77f6cSMauro Carvalho Chehab for (j = 0; j < SYS_FRAMES; j++) { 21295e950fafSDean Anderson vc->buffer.frame[j].ulState = 0; 21305e950fafSDean Anderson vc->buffer.frame[j].cur_size = 0; 21310aa77f6cSMauro Carvalho Chehab } 21320aa77f6cSMauro Carvalho Chehab 21330aa77f6cSMauro Carvalho Chehab /* send the start command */ 213447d8c881SDean Anderson buffer[0] = IN_DATA_TOKEN; 213547d8c881SDean Anderson buffer[1] = (__le32) cpu_to_le32(chn_rev); 213647d8c881SDean Anderson buffer[2] = CMD_START; 21370aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 21380aa77f6cSMauro Carvalho Chehab if (res != 0) 21390aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "CMD_START error\n"); 21400aa77f6cSMauro Carvalho Chehab 21415e950fafSDean Anderson dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res); 214247d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 21436a5b63b3SDean Anderson return res; 21440aa77f6cSMauro Carvalho Chehab } 21450aa77f6cSMauro Carvalho Chehab 21465e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc) 21470aa77f6cSMauro Carvalho Chehab { 21480aa77f6cSMauro Carvalho Chehab int res; 21490aa77f6cSMauro Carvalho Chehab unsigned long chn_rev; 21505e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 215147d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 215247d8c881SDean Anderson 215347d8c881SDean Anderson mutex_lock(&dev->cmdlock); 21545e950fafSDean Anderson chn_rev = G_chnmap[vc->idx]; 21550aa77f6cSMauro Carvalho Chehab /* send the stop command */ 215647d8c881SDean Anderson buffer[0] = IN_DATA_TOKEN; 215747d8c881SDean Anderson buffer[1] = (__le32) cpu_to_le32(chn_rev); 215847d8c881SDean Anderson buffer[2] = CMD_STOP; 215947d8c881SDean Anderson 21600aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 21610aa77f6cSMauro Carvalho Chehab if (res != 0) 21620aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "CMD_STOP error\n"); 216347d8c881SDean Anderson 21645e950fafSDean Anderson dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res); 216547d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 21660aa77f6cSMauro Carvalho Chehab return res; 21670aa77f6cSMauro Carvalho Chehab } 21680aa77f6cSMauro Carvalho Chehab 21690aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev) 21700aa77f6cSMauro Carvalho Chehab { 21710aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe = &dev->pipe; 21720aa77f6cSMauro Carvalho Chehab 21730aa77f6cSMauro Carvalho Chehab pipe->state = 0; 21740aa77f6cSMauro Carvalho Chehab if (pipe->stream_urb) { 21750aa77f6cSMauro Carvalho Chehab /* cancel urb */ 21760aa77f6cSMauro Carvalho Chehab usb_kill_urb(pipe->stream_urb); 21770aa77f6cSMauro Carvalho Chehab usb_free_urb(pipe->stream_urb); 21780aa77f6cSMauro Carvalho Chehab pipe->stream_urb = NULL; 21790aa77f6cSMauro Carvalho Chehab } 2180f5402007Ssensoray-dev dprintk(dev, 4, "%s", __func__); 21810aa77f6cSMauro Carvalho Chehab return; 21820aa77f6cSMauro Carvalho Chehab } 21830aa77f6cSMauro Carvalho Chehab 2184e2a06704SDean A static void s2255_fwload_start(struct s2255_dev *dev) 21850aa77f6cSMauro Carvalho Chehab { 21860aa77f6cSMauro Carvalho Chehab s2255_reset_dsppower(dev); 21870aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_size = dev->fw_data->fw->size; 21880aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); 21890aa77f6cSMauro Carvalho Chehab memcpy(dev->fw_data->pfw_data, 21900aa77f6cSMauro Carvalho Chehab dev->fw_data->fw->data, CHUNK_SIZE); 21910aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_loaded = CHUNK_SIZE; 21920aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, 21930aa77f6cSMauro Carvalho Chehab usb_sndbulkpipe(dev->udev, 2), 21940aa77f6cSMauro Carvalho Chehab dev->fw_data->pfw_data, 21950aa77f6cSMauro Carvalho Chehab CHUNK_SIZE, s2255_fwchunk_complete, 21960aa77f6cSMauro Carvalho Chehab dev->fw_data); 21970aa77f6cSMauro Carvalho Chehab mod_timer(&dev->timer, jiffies + HZ); 21980aa77f6cSMauro Carvalho Chehab } 21990aa77f6cSMauro Carvalho Chehab 22000aa77f6cSMauro Carvalho Chehab /* standard usb probe function */ 22010aa77f6cSMauro Carvalho Chehab static int s2255_probe(struct usb_interface *interface, 22020aa77f6cSMauro Carvalho Chehab const struct usb_device_id *id) 22030aa77f6cSMauro Carvalho Chehab { 22040aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev = NULL; 22050aa77f6cSMauro Carvalho Chehab struct usb_host_interface *iface_desc; 22060aa77f6cSMauro Carvalho Chehab struct usb_endpoint_descriptor *endpoint; 22070aa77f6cSMauro Carvalho Chehab int i; 22080aa77f6cSMauro Carvalho Chehab int retval = -ENOMEM; 22090aa77f6cSMauro Carvalho Chehab __le32 *pdata; 22100aa77f6cSMauro Carvalho Chehab int fw_size; 221147d8c881SDean Anderson 22120aa77f6cSMauro Carvalho Chehab /* allocate memory for our device state and initialize it to zero */ 22130aa77f6cSMauro Carvalho Chehab dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); 22140aa77f6cSMauro Carvalho Chehab if (dev == NULL) { 22150aa77f6cSMauro Carvalho Chehab s2255_dev_err(&interface->dev, "out of memory\n"); 22160aa77f6cSMauro Carvalho Chehab return -ENOMEM; 22170aa77f6cSMauro Carvalho Chehab } 221847d8c881SDean Anderson 221947d8c881SDean Anderson dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); 222047d8c881SDean Anderson if (dev->cmdbuf == NULL) { 222147d8c881SDean Anderson s2255_dev_err(&interface->dev, "out of memory\n"); 2222e21c94e7SDaeseok Youn goto errorFWDATA1; 222347d8c881SDean Anderson } 222447d8c881SDean Anderson 22250aa77f6cSMauro Carvalho Chehab atomic_set(&dev->num_channels, 0); 2226ff3ec57dSHans Verkuil dev->pid = id->idProduct; 22270aa77f6cSMauro Carvalho Chehab dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); 22280aa77f6cSMauro Carvalho Chehab if (!dev->fw_data) 22290aa77f6cSMauro Carvalho Chehab goto errorFWDATA1; 22300aa77f6cSMauro Carvalho Chehab mutex_init(&dev->lock); 223147d8c881SDean Anderson mutex_init(&dev->cmdlock); 22320aa77f6cSMauro Carvalho Chehab /* grab usb_device and save it */ 22330aa77f6cSMauro Carvalho Chehab dev->udev = usb_get_dev(interface_to_usbdev(interface)); 22340aa77f6cSMauro Carvalho Chehab if (dev->udev == NULL) { 22350aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "null usb device\n"); 22360aa77f6cSMauro Carvalho Chehab retval = -ENODEV; 22370aa77f6cSMauro Carvalho Chehab goto errorUDEV; 22380aa77f6cSMauro Carvalho Chehab } 2239f5402007Ssensoray-dev dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n", 2240f5402007Ssensoray-dev dev, dev->udev, interface); 22410aa77f6cSMauro Carvalho Chehab dev->interface = interface; 22420aa77f6cSMauro Carvalho Chehab /* set up the endpoint information */ 22430aa77f6cSMauro Carvalho Chehab iface_desc = interface->cur_altsetting; 2244f5402007Ssensoray-dev dev_dbg(&interface->dev, "num EP: %d\n", 2245f5402007Ssensoray-dev iface_desc->desc.bNumEndpoints); 22460aa77f6cSMauro Carvalho Chehab for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 22470aa77f6cSMauro Carvalho Chehab endpoint = &iface_desc->endpoint[i].desc; 22480aa77f6cSMauro Carvalho Chehab if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { 22490aa77f6cSMauro Carvalho Chehab /* we found the bulk in endpoint */ 22500aa77f6cSMauro Carvalho Chehab dev->read_endpoint = endpoint->bEndpointAddress; 22510aa77f6cSMauro Carvalho Chehab } 22520aa77f6cSMauro Carvalho Chehab } 22530aa77f6cSMauro Carvalho Chehab 22540aa77f6cSMauro Carvalho Chehab if (!dev->read_endpoint) { 22550aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); 22560aa77f6cSMauro Carvalho Chehab goto errorEP; 22570aa77f6cSMauro Carvalho Chehab } 225874ee0477SKees Cook timer_setup(&dev->timer, s2255_timer, 0); 22590aa77f6cSMauro Carvalho Chehab init_waitqueue_head(&dev->fw_data->wait_fw); 22600aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 22615e950fafSDean Anderson struct s2255_vc *vc = &dev->vc[i]; 22625e950fafSDean Anderson vc->idx = i; 22635e950fafSDean Anderson vc->dev = dev; 22645e950fafSDean Anderson init_waitqueue_head(&vc->wait_setmode); 22655e950fafSDean Anderson init_waitqueue_head(&vc->wait_vidstatus); 2266340a30c5Ssensoray-dev spin_lock_init(&vc->qlock); 2267340a30c5Ssensoray-dev mutex_init(&vc->vb_lock); 22680aa77f6cSMauro Carvalho Chehab } 22690aa77f6cSMauro Carvalho Chehab 22700aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); 2271fc56da79SWolfram Sang if (!dev->fw_data->fw_urb) 22720aa77f6cSMauro Carvalho Chehab goto errorFWURB; 22730aa77f6cSMauro Carvalho Chehab 22740aa77f6cSMauro Carvalho Chehab dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); 22750aa77f6cSMauro Carvalho Chehab if (!dev->fw_data->pfw_data) { 22760aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "out of memory!\n"); 22770aa77f6cSMauro Carvalho Chehab goto errorFWDATA2; 22780aa77f6cSMauro Carvalho Chehab } 22790aa77f6cSMauro Carvalho Chehab /* load the first chunk */ 22800aa77f6cSMauro Carvalho Chehab if (request_firmware(&dev->fw_data->fw, 22810aa77f6cSMauro Carvalho Chehab FIRMWARE_FILE_NAME, &dev->udev->dev)) { 2282f5402007Ssensoray-dev dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n"); 22830aa77f6cSMauro Carvalho Chehab goto errorREQFW; 22840aa77f6cSMauro Carvalho Chehab } 22850aa77f6cSMauro Carvalho Chehab /* check the firmware is valid */ 22860aa77f6cSMauro Carvalho Chehab fw_size = dev->fw_data->fw->size; 22870aa77f6cSMauro Carvalho Chehab pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; 22880aa77f6cSMauro Carvalho Chehab 22890aa77f6cSMauro Carvalho Chehab if (*pdata != S2255_FW_MARKER) { 2290f5402007Ssensoray-dev dev_err(&interface->dev, "Firmware invalid.\n"); 22910aa77f6cSMauro Carvalho Chehab retval = -ENODEV; 22920aa77f6cSMauro Carvalho Chehab goto errorFWMARKER; 22930aa77f6cSMauro Carvalho Chehab } else { 22940aa77f6cSMauro Carvalho Chehab /* make sure firmware is the latest */ 22950aa77f6cSMauro Carvalho Chehab __le32 *pRel; 22960aa77f6cSMauro Carvalho Chehab pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; 2297f5402007Ssensoray-dev pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel)); 22980aa77f6cSMauro Carvalho Chehab dev->dsp_fw_ver = le32_to_cpu(*pRel); 22990aa77f6cSMauro Carvalho Chehab if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) 2300f5402007Ssensoray-dev pr_info("s2255: f2255usb.bin out of date.\n"); 23010aa77f6cSMauro Carvalho Chehab if (dev->pid == 0x2257 && 23020aa77f6cSMauro Carvalho Chehab dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) 2303f5402007Ssensoray-dev pr_warn("2257 needs firmware %d or above.\n", 2304f5402007Ssensoray-dev S2255_MIN_DSP_COLORFILTER); 23050aa77f6cSMauro Carvalho Chehab } 23060aa77f6cSMauro Carvalho Chehab usb_reset_device(dev->udev); 23070aa77f6cSMauro Carvalho Chehab /* load 2255 board specific */ 23080aa77f6cSMauro Carvalho Chehab retval = s2255_board_init(dev); 23090aa77f6cSMauro Carvalho Chehab if (retval) 23100aa77f6cSMauro Carvalho Chehab goto errorBOARDINIT; 2311e2a06704SDean A s2255_fwload_start(dev); 23120aa77f6cSMauro Carvalho Chehab /* loads v4l specific */ 23130aa77f6cSMauro Carvalho Chehab retval = s2255_probe_v4l(dev); 23140aa77f6cSMauro Carvalho Chehab if (retval) 23150aa77f6cSMauro Carvalho Chehab goto errorBOARDINIT; 23160aa77f6cSMauro Carvalho Chehab dev_info(&interface->dev, "Sensoray 2255 detected\n"); 23170aa77f6cSMauro Carvalho Chehab return 0; 23180aa77f6cSMauro Carvalho Chehab errorBOARDINIT: 23190aa77f6cSMauro Carvalho Chehab s2255_board_shutdown(dev); 23200aa77f6cSMauro Carvalho Chehab errorFWMARKER: 23210aa77f6cSMauro Carvalho Chehab release_firmware(dev->fw_data->fw); 23220aa77f6cSMauro Carvalho Chehab errorREQFW: 23230aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data->pfw_data); 23240aa77f6cSMauro Carvalho Chehab errorFWDATA2: 23250aa77f6cSMauro Carvalho Chehab usb_free_urb(dev->fw_data->fw_urb); 23260aa77f6cSMauro Carvalho Chehab errorFWURB: 23279f6be2bcSKirill Tkhai del_timer_sync(&dev->timer); 23280aa77f6cSMauro Carvalho Chehab errorEP: 23290aa77f6cSMauro Carvalho Chehab usb_put_dev(dev->udev); 23300aa77f6cSMauro Carvalho Chehab errorUDEV: 23310aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data); 23320aa77f6cSMauro Carvalho Chehab mutex_destroy(&dev->lock); 23330aa77f6cSMauro Carvalho Chehab errorFWDATA1: 233447d8c881SDean Anderson kfree(dev->cmdbuf); 23350aa77f6cSMauro Carvalho Chehab kfree(dev); 2336f5402007Ssensoray-dev pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval); 23370aa77f6cSMauro Carvalho Chehab return retval; 23380aa77f6cSMauro Carvalho Chehab } 23390aa77f6cSMauro Carvalho Chehab 23400aa77f6cSMauro Carvalho Chehab /* disconnect routine. when board is removed physically or with rmmod */ 23410aa77f6cSMauro Carvalho Chehab static void s2255_disconnect(struct usb_interface *interface) 23420aa77f6cSMauro Carvalho Chehab { 23430aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); 23440aa77f6cSMauro Carvalho Chehab int i; 23450aa77f6cSMauro Carvalho Chehab int channels = atomic_read(&dev->num_channels); 23460aa77f6cSMauro Carvalho Chehab mutex_lock(&dev->lock); 23470aa77f6cSMauro Carvalho Chehab v4l2_device_disconnect(&dev->v4l2_dev); 23480aa77f6cSMauro Carvalho Chehab mutex_unlock(&dev->lock); 23490aa77f6cSMauro Carvalho Chehab /*see comments in the uvc_driver.c usb disconnect function */ 23500aa77f6cSMauro Carvalho Chehab atomic_inc(&dev->num_channels); 23510aa77f6cSMauro Carvalho Chehab /* unregister each video device. */ 23520aa77f6cSMauro Carvalho Chehab for (i = 0; i < channels; i++) 23535e950fafSDean Anderson video_unregister_device(&dev->vc[i].vdev); 23540aa77f6cSMauro Carvalho Chehab /* wake up any of our timers */ 23550aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); 23560aa77f6cSMauro Carvalho Chehab wake_up(&dev->fw_data->wait_fw); 23570aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 23585e950fafSDean Anderson dev->vc[i].setmode_ready = 1; 23595e950fafSDean Anderson wake_up(&dev->vc[i].wait_setmode); 23605e950fafSDean Anderson dev->vc[i].vidstatus_ready = 1; 23615e950fafSDean Anderson wake_up(&dev->vc[i].wait_vidstatus); 23620aa77f6cSMauro Carvalho Chehab } 23630aa77f6cSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->num_channels)) 23640aa77f6cSMauro Carvalho Chehab s2255_destroy(dev); 23650aa77f6cSMauro Carvalho Chehab dev_info(&interface->dev, "%s\n", __func__); 23660aa77f6cSMauro Carvalho Chehab } 23670aa77f6cSMauro Carvalho Chehab 23680aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver = { 23690aa77f6cSMauro Carvalho Chehab .name = S2255_DRIVER_NAME, 23700aa77f6cSMauro Carvalho Chehab .probe = s2255_probe, 23710aa77f6cSMauro Carvalho Chehab .disconnect = s2255_disconnect, 23720aa77f6cSMauro Carvalho Chehab .id_table = s2255_table, 23730aa77f6cSMauro Carvalho Chehab }; 23740aa77f6cSMauro Carvalho Chehab 23750aa77f6cSMauro Carvalho Chehab module_usb_driver(s2255_driver); 23760aa77f6cSMauro Carvalho Chehab 23770aa77f6cSMauro Carvalho Chehab MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); 23780aa77f6cSMauro Carvalho Chehab MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); 23790aa77f6cSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 23800aa77f6cSMauro Carvalho Chehab MODULE_VERSION(S2255_VERSION); 23810aa77f6cSMauro Carvalho Chehab MODULE_FIRMWARE(FIRMWARE_FILE_NAME); 2382