10aa77f6cSMauro Carvalho Chehab /* 20aa77f6cSMauro Carvalho Chehab * s2255drv.c - a driver for the Sensoray 2255 USB video capture device 30aa77f6cSMauro Carvalho Chehab * 4d86c6a8cSDean Anderson * Copyright (C) 2007-2014 by Sensoray Company Inc. 50aa77f6cSMauro Carvalho Chehab * Dean Anderson 60aa77f6cSMauro Carvalho Chehab * 70aa77f6cSMauro Carvalho Chehab * Some video buffer code based on vivi driver: 80aa77f6cSMauro Carvalho Chehab * 90aa77f6cSMauro Carvalho Chehab * Sensoray 2255 device supports 4 simultaneous channels. 100aa77f6cSMauro Carvalho Chehab * The channels are not "crossbar" inputs, they are physically 110aa77f6cSMauro Carvalho Chehab * attached to separate video decoders. 120aa77f6cSMauro Carvalho Chehab * 130aa77f6cSMauro Carvalho Chehab * Because of USB2.0 bandwidth limitations. There is only a 140aa77f6cSMauro Carvalho Chehab * certain amount of data which may be transferred at one time. 150aa77f6cSMauro Carvalho Chehab * 160aa77f6cSMauro Carvalho Chehab * Example maximum bandwidth utilization: 170aa77f6cSMauro Carvalho Chehab * 180aa77f6cSMauro Carvalho Chehab * -full size, color mode YUYV or YUV422P: 2 channels at once 190aa77f6cSMauro Carvalho Chehab * -full or half size Grey scale: all 4 channels at once 200aa77f6cSMauro Carvalho Chehab * -half size, color mode YUYV or YUV422P: all 4 channels at once 210aa77f6cSMauro Carvalho Chehab * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels 220aa77f6cSMauro Carvalho Chehab * at once. 230aa77f6cSMauro Carvalho Chehab * 240aa77f6cSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 250aa77f6cSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 260aa77f6cSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 270aa77f6cSMauro Carvalho Chehab * (at your option) any later version. 280aa77f6cSMauro Carvalho Chehab * 290aa77f6cSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 300aa77f6cSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 310aa77f6cSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 320aa77f6cSMauro Carvalho Chehab * GNU General Public License for more details. 330aa77f6cSMauro Carvalho Chehab * 340aa77f6cSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 350aa77f6cSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 360aa77f6cSMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 370aa77f6cSMauro Carvalho Chehab */ 380aa77f6cSMauro Carvalho Chehab 390aa77f6cSMauro Carvalho Chehab #include <linux/module.h> 400aa77f6cSMauro Carvalho Chehab #include <linux/firmware.h> 410aa77f6cSMauro Carvalho Chehab #include <linux/kernel.h> 420aa77f6cSMauro Carvalho Chehab #include <linux/mutex.h> 430aa77f6cSMauro Carvalho Chehab #include <linux/slab.h> 440aa77f6cSMauro Carvalho Chehab #include <linux/videodev2.h> 450aa77f6cSMauro Carvalho Chehab #include <linux/mm.h> 4644d06d82SHans Verkuil #include <linux/vmalloc.h> 4744d06d82SHans Verkuil #include <linux/usb.h> 48340a30c5Ssensoray-dev #include <media/videobuf2-vmalloc.h> 490aa77f6cSMauro Carvalho Chehab #include <media/v4l2-common.h> 500aa77f6cSMauro Carvalho Chehab #include <media/v4l2-device.h> 510aa77f6cSMauro Carvalho Chehab #include <media/v4l2-ioctl.h> 52192f1e78SHans Verkuil #include <media/v4l2-ctrls.h> 5344d06d82SHans Verkuil #include <media/v4l2-event.h> 540aa77f6cSMauro Carvalho Chehab 55340a30c5Ssensoray-dev #define S2255_VERSION "1.25.1" 560aa77f6cSMauro Carvalho Chehab #define FIRMWARE_FILE_NAME "f2255usb.bin" 570aa77f6cSMauro Carvalho Chehab 580aa77f6cSMauro Carvalho Chehab /* default JPEG quality */ 590aa77f6cSMauro Carvalho Chehab #define S2255_DEF_JPEG_QUAL 50 600aa77f6cSMauro Carvalho Chehab /* vendor request in */ 610aa77f6cSMauro Carvalho Chehab #define S2255_VR_IN 0 620aa77f6cSMauro Carvalho Chehab /* vendor request out */ 630aa77f6cSMauro Carvalho Chehab #define S2255_VR_OUT 1 640aa77f6cSMauro Carvalho Chehab /* firmware query */ 650aa77f6cSMauro Carvalho Chehab #define S2255_VR_FW 0x30 660aa77f6cSMauro Carvalho Chehab /* USB endpoint number for configuring the device */ 670aa77f6cSMauro Carvalho Chehab #define S2255_CONFIG_EP 2 680aa77f6cSMauro Carvalho Chehab /* maximum time for DSP to start responding after last FW word loaded(ms) */ 690aa77f6cSMauro Carvalho Chehab #define S2255_DSP_BOOTTIME 800 700aa77f6cSMauro Carvalho Chehab /* maximum time to wait for firmware to load (ms) */ 710aa77f6cSMauro Carvalho Chehab #define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) 729da62eb0SDean Anderson #define S2255_MIN_BUFS 2 730aa77f6cSMauro Carvalho Chehab #define S2255_SETMODE_TIMEOUT 500 740aa77f6cSMauro Carvalho Chehab #define S2255_VIDSTATUS_TIMEOUT 350 750aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) 760aa77f6cSMauro Carvalho Chehab #define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) 770aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) 780aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_FW cpu_to_le32(0x10) 790aa77f6cSMauro Carvalho Chehab #define S2255_RESPONSE_STATUS cpu_to_le32(0x20) 800aa77f6cSMauro Carvalho Chehab #define S2255_USB_XFER_SIZE (16 * 1024) 810aa77f6cSMauro Carvalho Chehab #define MAX_CHANNELS 4 820aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES 4 830aa77f6cSMauro Carvalho Chehab /* maximum size is PAL full size plus room for the marker header(s) */ 840aa77f6cSMauro Carvalho Chehab #define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) 850aa77f6cSMauro Carvalho Chehab #define DEF_USB_BLOCK S2255_USB_XFER_SIZE 860aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_NTSC 640 870aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_NTSC 640 880aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_NTSC 320 890aa77f6cSMauro Carvalho Chehab #define LINE_SZ_4CIFS_PAL 704 900aa77f6cSMauro Carvalho Chehab #define LINE_SZ_2CIFS_PAL 704 910aa77f6cSMauro Carvalho Chehab #define LINE_SZ_1CIFS_PAL 352 920aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_NTSC 240 930aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_NTSC 240 940aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_NTSC 240 950aa77f6cSMauro Carvalho Chehab #define NUM_LINES_4CIFS_PAL 288 960aa77f6cSMauro Carvalho Chehab #define NUM_LINES_2CIFS_PAL 288 970aa77f6cSMauro Carvalho Chehab #define NUM_LINES_1CIFS_PAL 288 980aa77f6cSMauro Carvalho Chehab #define LINE_SZ_DEF 640 990aa77f6cSMauro Carvalho Chehab #define NUM_LINES_DEF 240 1000aa77f6cSMauro Carvalho Chehab 1010aa77f6cSMauro Carvalho Chehab 1020aa77f6cSMauro Carvalho Chehab /* predefined settings */ 1030aa77f6cSMauro Carvalho Chehab #define FORMAT_NTSC 1 1040aa77f6cSMauro Carvalho Chehab #define FORMAT_PAL 2 1050aa77f6cSMauro Carvalho Chehab 1060aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ 1070aa77f6cSMauro Carvalho Chehab #define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ 1080aa77f6cSMauro Carvalho Chehab #define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ 1090aa77f6cSMauro Carvalho Chehab /* SCALE_4CIFSI is the 2 fields interpolated into one */ 1100aa77f6cSMauro Carvalho Chehab #define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ 1110aa77f6cSMauro Carvalho Chehab 1120aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPL 1 /* YUV planar */ 1130aa77f6cSMauro Carvalho Chehab #define COLOR_YUVPK 2 /* YUV packed */ 1140aa77f6cSMauro Carvalho Chehab #define COLOR_Y8 4 /* monochrome */ 1150aa77f6cSMauro Carvalho Chehab #define COLOR_JPG 5 /* JPEG */ 1160aa77f6cSMauro Carvalho Chehab 1170aa77f6cSMauro Carvalho Chehab #define MASK_COLOR 0x000000ff 1180aa77f6cSMauro Carvalho Chehab #define MASK_JPG_QUALITY 0x0000ff00 1190aa77f6cSMauro Carvalho Chehab #define MASK_INPUT_TYPE 0x000f0000 1200aa77f6cSMauro Carvalho Chehab /* frame decimation. */ 1210aa77f6cSMauro Carvalho Chehab #define FDEC_1 1 /* capture every frame. default */ 1220aa77f6cSMauro Carvalho Chehab #define FDEC_2 2 /* capture every 2nd frame */ 1230aa77f6cSMauro Carvalho Chehab #define FDEC_3 3 /* capture every 3rd frame */ 1240aa77f6cSMauro Carvalho Chehab #define FDEC_5 5 /* capture every 5th frame */ 1250aa77f6cSMauro Carvalho Chehab 1260aa77f6cSMauro Carvalho Chehab /*------------------------------------------------------- 1270aa77f6cSMauro Carvalho Chehab * Default mode parameters. 1280aa77f6cSMauro Carvalho Chehab *-------------------------------------------------------*/ 1290aa77f6cSMauro Carvalho Chehab #define DEF_SCALE SCALE_4CIFS 1300aa77f6cSMauro Carvalho Chehab #define DEF_COLOR COLOR_YUVPL 1310aa77f6cSMauro Carvalho Chehab #define DEF_FDEC FDEC_1 1320aa77f6cSMauro Carvalho Chehab #define DEF_BRIGHT 0 1330aa77f6cSMauro Carvalho Chehab #define DEF_CONTRAST 0x5c 1340aa77f6cSMauro Carvalho Chehab #define DEF_SATURATION 0x80 1350aa77f6cSMauro Carvalho Chehab #define DEF_HUE 0 1360aa77f6cSMauro Carvalho Chehab 1370aa77f6cSMauro Carvalho Chehab /* usb config commands */ 1380aa77f6cSMauro Carvalho Chehab #define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) 1390aa77f6cSMauro Carvalho Chehab #define CMD_2255 0xc2255000 1400aa77f6cSMauro Carvalho Chehab #define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) 1410aa77f6cSMauro Carvalho Chehab #define CMD_START cpu_to_le32((CMD_2255 | 0x20)) 1420aa77f6cSMauro Carvalho Chehab #define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) 1430aa77f6cSMauro Carvalho Chehab #define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) 1440aa77f6cSMauro Carvalho Chehab 1450aa77f6cSMauro Carvalho Chehab struct s2255_mode { 1460aa77f6cSMauro Carvalho Chehab u32 format; /* input video format (NTSC, PAL) */ 1470aa77f6cSMauro Carvalho Chehab u32 scale; /* output video scale */ 1480aa77f6cSMauro Carvalho Chehab u32 color; /* output video color format */ 1490aa77f6cSMauro Carvalho Chehab u32 fdec; /* frame decimation */ 1500aa77f6cSMauro Carvalho Chehab u32 bright; /* brightness */ 1510aa77f6cSMauro Carvalho Chehab u32 contrast; /* contrast */ 1520aa77f6cSMauro Carvalho Chehab u32 saturation; /* saturation */ 1530aa77f6cSMauro Carvalho Chehab u32 hue; /* hue (NTSC only)*/ 1540aa77f6cSMauro Carvalho Chehab u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ 1550aa77f6cSMauro Carvalho Chehab u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ 1560aa77f6cSMauro Carvalho Chehab u32 restart; /* if DSP requires restart */ 1570aa77f6cSMauro Carvalho Chehab }; 1580aa77f6cSMauro Carvalho Chehab 1590aa77f6cSMauro Carvalho Chehab 1600aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE 0 1610aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME 1 1620aa77f6cSMauro Carvalho Chehab 1630aa77f6cSMauro Carvalho Chehab /* frame structure */ 1640aa77f6cSMauro Carvalho Chehab struct s2255_framei { 1650aa77f6cSMauro Carvalho Chehab unsigned long size; 1660aa77f6cSMauro Carvalho Chehab unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ 1670aa77f6cSMauro Carvalho Chehab void *lpvbits; /* image data */ 1680aa77f6cSMauro Carvalho Chehab unsigned long cur_size; /* current data copied to it */ 1690aa77f6cSMauro Carvalho Chehab }; 1700aa77f6cSMauro Carvalho Chehab 1710aa77f6cSMauro Carvalho Chehab /* image buffer structure */ 1720aa77f6cSMauro Carvalho Chehab struct s2255_bufferi { 1730aa77f6cSMauro Carvalho Chehab unsigned long dwFrames; /* number of frames in buffer */ 1740aa77f6cSMauro Carvalho Chehab struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ 1750aa77f6cSMauro Carvalho Chehab }; 1760aa77f6cSMauro Carvalho Chehab 1770aa77f6cSMauro Carvalho Chehab #define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ 1780aa77f6cSMauro Carvalho Chehab DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ 1790aa77f6cSMauro Carvalho Chehab DEF_HUE, 0, DEF_USB_BLOCK, 0} 1800aa77f6cSMauro Carvalho Chehab 1810aa77f6cSMauro Carvalho Chehab /* for firmware loading, fw_state */ 1820aa77f6cSMauro Carvalho Chehab #define S2255_FW_NOTLOADED 0 1830aa77f6cSMauro Carvalho Chehab #define S2255_FW_LOADED_DSPWAIT 1 1840aa77f6cSMauro Carvalho Chehab #define S2255_FW_SUCCESS 2 1850aa77f6cSMauro Carvalho Chehab #define S2255_FW_FAILED 3 1860aa77f6cSMauro Carvalho Chehab #define S2255_FW_DISCONNECTING 4 1870aa77f6cSMauro Carvalho Chehab #define S2255_FW_MARKER cpu_to_le32(0x22552f2f) 1880aa77f6cSMauro Carvalho Chehab /* 2255 read states */ 1890aa77f6cSMauro Carvalho Chehab #define S2255_READ_IDLE 0 1900aa77f6cSMauro Carvalho Chehab #define S2255_READ_FRAME 1 1910aa77f6cSMauro Carvalho Chehab struct s2255_fw { 1920aa77f6cSMauro Carvalho Chehab int fw_loaded; 1930aa77f6cSMauro Carvalho Chehab int fw_size; 1940aa77f6cSMauro Carvalho Chehab struct urb *fw_urb; 1950aa77f6cSMauro Carvalho Chehab atomic_t fw_state; 1960aa77f6cSMauro Carvalho Chehab void *pfw_data; 1970aa77f6cSMauro Carvalho Chehab wait_queue_head_t wait_fw; 1980aa77f6cSMauro Carvalho Chehab const struct firmware *fw; 1990aa77f6cSMauro Carvalho Chehab }; 2000aa77f6cSMauro Carvalho Chehab 2010aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo { 2020aa77f6cSMauro Carvalho Chehab u32 max_transfer_size; 2030aa77f6cSMauro Carvalho Chehab u32 cur_transfer_size; 2040aa77f6cSMauro Carvalho Chehab u8 *transfer_buffer; 2050aa77f6cSMauro Carvalho Chehab u32 state; 2060aa77f6cSMauro Carvalho Chehab void *stream_urb; 2070aa77f6cSMauro Carvalho Chehab void *dev; /* back pointer to s2255_dev struct*/ 2080aa77f6cSMauro Carvalho Chehab u32 err_count; 2090aa77f6cSMauro Carvalho Chehab u32 idx; 2100aa77f6cSMauro Carvalho Chehab }; 2110aa77f6cSMauro Carvalho Chehab 2120aa77f6cSMauro Carvalho Chehab struct s2255_fmt; /*forward declaration */ 2130aa77f6cSMauro Carvalho Chehab struct s2255_dev; 2140aa77f6cSMauro Carvalho Chehab 2155e950fafSDean Anderson /* 2255 video channel */ 2165e950fafSDean Anderson struct s2255_vc { 217f5402007Ssensoray-dev struct s2255_dev *dev; 2180aa77f6cSMauro Carvalho Chehab struct video_device vdev; 219192f1e78SHans Verkuil struct v4l2_ctrl_handler hdl; 2207041dec7SHans Verkuil struct v4l2_ctrl *jpegqual_ctrl; 2210aa77f6cSMauro Carvalho Chehab int resources; 222d86c6a8cSDean Anderson struct list_head buf_list; 2230aa77f6cSMauro Carvalho Chehab struct s2255_bufferi buffer; 2240aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 225469af77aSHans Verkuil v4l2_std_id std; 2260aa77f6cSMauro Carvalho Chehab /* jpeg compression */ 2277041dec7SHans Verkuil unsigned jpegqual; 2280aa77f6cSMauro Carvalho Chehab /* capture parameters (for high quality mode full size) */ 2290aa77f6cSMauro Carvalho Chehab struct v4l2_captureparm cap_parm; 2300aa77f6cSMauro Carvalho Chehab int cur_frame; 2310aa77f6cSMauro Carvalho Chehab int last_frame; 2320aa77f6cSMauro Carvalho Chehab /* allocated image size */ 2330aa77f6cSMauro Carvalho Chehab unsigned long req_image_size; 2340aa77f6cSMauro Carvalho Chehab /* received packet size */ 2350aa77f6cSMauro Carvalho Chehab unsigned long pkt_size; 2360aa77f6cSMauro Carvalho Chehab int bad_payload; 2370aa77f6cSMauro Carvalho Chehab unsigned long frame_count; 2380aa77f6cSMauro Carvalho Chehab /* if JPEG image */ 2390aa77f6cSMauro Carvalho Chehab int jpg_size; 2400aa77f6cSMauro Carvalho Chehab /* if channel configured to default state */ 2410aa77f6cSMauro Carvalho Chehab int configured; 2420aa77f6cSMauro Carvalho Chehab wait_queue_head_t wait_setmode; 2430aa77f6cSMauro Carvalho Chehab int setmode_ready; 2440aa77f6cSMauro Carvalho Chehab /* video status items */ 2450aa77f6cSMauro Carvalho Chehab int vidstatus; 2460aa77f6cSMauro Carvalho Chehab wait_queue_head_t wait_vidstatus; 2470aa77f6cSMauro Carvalho Chehab int vidstatus_ready; 2480aa77f6cSMauro Carvalho Chehab unsigned int width; 2490aa77f6cSMauro Carvalho Chehab unsigned int height; 250340a30c5Ssensoray-dev enum v4l2_field field; 2510aa77f6cSMauro Carvalho Chehab const struct s2255_fmt *fmt; 2520aa77f6cSMauro Carvalho Chehab int idx; /* channel number on device, 0-3 */ 253340a30c5Ssensoray-dev struct vb2_queue vb_vidq; 254340a30c5Ssensoray-dev struct mutex vb_lock; /* streaming lock */ 255340a30c5Ssensoray-dev spinlock_t qlock; 2560aa77f6cSMauro Carvalho Chehab }; 2570aa77f6cSMauro Carvalho Chehab 2580aa77f6cSMauro Carvalho Chehab 2590aa77f6cSMauro Carvalho Chehab struct s2255_dev { 2605e950fafSDean Anderson struct s2255_vc vc[MAX_CHANNELS]; 2610aa77f6cSMauro Carvalho Chehab struct v4l2_device v4l2_dev; 2620aa77f6cSMauro Carvalho Chehab atomic_t num_channels; 2630aa77f6cSMauro Carvalho Chehab int frames; 2640aa77f6cSMauro Carvalho Chehab struct mutex lock; /* channels[].vdev.lock */ 26547d8c881SDean Anderson struct mutex cmdlock; /* protects cmdbuf */ 2660aa77f6cSMauro Carvalho Chehab struct usb_device *udev; 2670aa77f6cSMauro Carvalho Chehab struct usb_interface *interface; 2680aa77f6cSMauro Carvalho Chehab u8 read_endpoint; 2690aa77f6cSMauro Carvalho Chehab struct timer_list timer; 2700aa77f6cSMauro Carvalho Chehab struct s2255_fw *fw_data; 2710aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo pipe; 2720aa77f6cSMauro Carvalho Chehab u32 cc; /* current channel */ 2730aa77f6cSMauro Carvalho Chehab int frame_ready; 2740aa77f6cSMauro Carvalho Chehab int chn_ready; 2750aa77f6cSMauro Carvalho Chehab /* dsp firmware version (f2255usb.bin) */ 2760aa77f6cSMauro Carvalho Chehab int dsp_fw_ver; 2770aa77f6cSMauro Carvalho Chehab u16 pid; /* product id */ 27847d8c881SDean Anderson #define S2255_CMDBUF_SIZE 512 27947d8c881SDean Anderson __le32 *cmdbuf; 2800aa77f6cSMauro Carvalho Chehab }; 2810aa77f6cSMauro Carvalho Chehab 2820aa77f6cSMauro Carvalho Chehab static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) 2830aa77f6cSMauro Carvalho Chehab { 2840aa77f6cSMauro Carvalho Chehab return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); 2850aa77f6cSMauro Carvalho Chehab } 2860aa77f6cSMauro Carvalho Chehab 2870aa77f6cSMauro Carvalho Chehab struct s2255_fmt { 2880aa77f6cSMauro Carvalho Chehab char *name; 2890aa77f6cSMauro Carvalho Chehab u32 fourcc; 2900aa77f6cSMauro Carvalho Chehab int depth; 2910aa77f6cSMauro Carvalho Chehab }; 2920aa77f6cSMauro Carvalho Chehab 2930aa77f6cSMauro Carvalho Chehab /* buffer for one video frame */ 2940aa77f6cSMauro Carvalho Chehab struct s2255_buffer { 2950aa77f6cSMauro Carvalho Chehab /* common v4l buffer stuff -- must be first */ 296340a30c5Ssensoray-dev struct vb2_buffer vb; 297340a30c5Ssensoray-dev struct list_head list; 2980aa77f6cSMauro Carvalho Chehab }; 2990aa77f6cSMauro Carvalho Chehab 3000aa77f6cSMauro Carvalho Chehab 3010aa77f6cSMauro Carvalho Chehab /* current cypress EEPROM firmware version */ 3020aa77f6cSMauro Carvalho Chehab #define S2255_CUR_USB_FWVER ((3 << 8) | 12) 3030aa77f6cSMauro Carvalho Chehab /* current DSP FW version */ 3040aa77f6cSMauro Carvalho Chehab #define S2255_CUR_DSP_FWVER 10104 3050aa77f6cSMauro Carvalho Chehab /* Need DSP version 5+ for video status feature */ 3060aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_STATUS 5 3070aa77f6cSMauro Carvalho Chehab #define S2255_MIN_DSP_COLORFILTER 8 308469af77aSHans Verkuil #define S2255_NORMS (V4L2_STD_ALL) 3090aa77f6cSMauro Carvalho Chehab 3100aa77f6cSMauro Carvalho Chehab /* private V4L2 controls */ 3110aa77f6cSMauro Carvalho Chehab 3120aa77f6cSMauro Carvalho Chehab /* 3130aa77f6cSMauro Carvalho Chehab * The following chart displays how COLORFILTER should be set 3140aa77f6cSMauro Carvalho Chehab * ========================================================= 3150aa77f6cSMauro Carvalho Chehab * = fourcc = COLORFILTER = 3160aa77f6cSMauro Carvalho Chehab * = =============================== 3170aa77f6cSMauro Carvalho Chehab * = = 0 = 1 = 3180aa77f6cSMauro Carvalho Chehab * ========================================================= 3190aa77f6cSMauro Carvalho Chehab * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= 3200aa77f6cSMauro Carvalho Chehab * = = s-video or = composite = 3210aa77f6cSMauro Carvalho Chehab * = = B/W camera = input = 3220aa77f6cSMauro Carvalho Chehab * ========================================================= 3230aa77f6cSMauro Carvalho Chehab * = other = color, svideo = color, = 3240aa77f6cSMauro Carvalho Chehab * = = = composite = 3250aa77f6cSMauro Carvalho Chehab * ========================================================= 3260aa77f6cSMauro Carvalho Chehab * 3270aa77f6cSMauro Carvalho Chehab * Notes: 3280aa77f6cSMauro Carvalho Chehab * channels 0-3 on 2255 are composite 3290aa77f6cSMauro Carvalho Chehab * channels 0-1 on 2257 are composite, 2-3 are s-video 3300aa77f6cSMauro Carvalho Chehab * If COLORFILTER is 0 with a composite color camera connected, 3310aa77f6cSMauro Carvalho Chehab * the output will appear monochrome but hatching 3320aa77f6cSMauro Carvalho Chehab * will occur. 3330aa77f6cSMauro Carvalho Chehab * COLORFILTER is different from "color killer" and "color effects" 3340aa77f6cSMauro Carvalho Chehab * for reasons above. 3350aa77f6cSMauro Carvalho Chehab */ 3360aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_ON 1 3370aa77f6cSMauro Carvalho Chehab #define S2255_V4L2_YC_OFF 0 338192f1e78SHans Verkuil #define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0) 3390aa77f6cSMauro Carvalho Chehab 3400aa77f6cSMauro Carvalho Chehab /* frame prefix size (sent once every frame) */ 3410aa77f6cSMauro Carvalho Chehab #define PREFIX_SIZE 512 3420aa77f6cSMauro Carvalho Chehab 3430aa77f6cSMauro Carvalho Chehab /* Channels on box are in reverse order */ 3440aa77f6cSMauro Carvalho Chehab static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; 3450aa77f6cSMauro Carvalho Chehab 3460aa77f6cSMauro Carvalho Chehab static int debug; 3470aa77f6cSMauro Carvalho Chehab 3480aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev); 3490aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev); 3505e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc); 3515e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc); 3525e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf, 3530aa77f6cSMauro Carvalho Chehab int jpgsize); 3545e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode); 3550aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev); 3560aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset); 3570aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev); 3580aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, 3590aa77f6cSMauro Carvalho Chehab u16 index, u16 value, void *buf, 3600aa77f6cSMauro Carvalho Chehab s32 buf_len, int bOut); 3610aa77f6cSMauro Carvalho Chehab 3620aa77f6cSMauro Carvalho Chehab /* dev_err macro with driver name */ 3630aa77f6cSMauro Carvalho Chehab #define S2255_DRIVER_NAME "s2255" 3640aa77f6cSMauro Carvalho Chehab #define s2255_dev_err(dev, fmt, arg...) \ 3650aa77f6cSMauro Carvalho Chehab dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) 3660aa77f6cSMauro Carvalho Chehab 367f5402007Ssensoray-dev #define dprintk(dev, level, fmt, arg...) \ 368f5402007Ssensoray-dev v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) 3690aa77f6cSMauro Carvalho Chehab 3700aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver; 3710aa77f6cSMauro Carvalho Chehab 3720aa77f6cSMauro Carvalho Chehab /* start video number */ 3730aa77f6cSMauro Carvalho Chehab static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ 3740aa77f6cSMauro Carvalho Chehab 3750aa77f6cSMauro Carvalho Chehab /* Enable jpeg capture. */ 3760aa77f6cSMauro Carvalho Chehab static int jpeg_enable = 1; 3770aa77f6cSMauro Carvalho Chehab 3780aa77f6cSMauro Carvalho Chehab module_param(debug, int, 0644); 3790aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); 3800aa77f6cSMauro Carvalho Chehab module_param(video_nr, int, 0644); 3810aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); 3820aa77f6cSMauro Carvalho Chehab module_param(jpeg_enable, int, 0644); 3830aa77f6cSMauro Carvalho Chehab MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); 3840aa77f6cSMauro Carvalho Chehab 3850aa77f6cSMauro Carvalho Chehab /* USB device table */ 3860aa77f6cSMauro Carvalho Chehab #define USB_SENSORAY_VID 0x1943 3870aa77f6cSMauro Carvalho Chehab static struct usb_device_id s2255_table[] = { 3880aa77f6cSMauro Carvalho Chehab {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, 3890aa77f6cSMauro Carvalho Chehab {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ 3900aa77f6cSMauro Carvalho Chehab { } /* Terminating entry */ 3910aa77f6cSMauro Carvalho Chehab }; 3920aa77f6cSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, s2255_table); 3930aa77f6cSMauro Carvalho Chehab 3940aa77f6cSMauro Carvalho Chehab #define BUFFER_TIMEOUT msecs_to_jiffies(400) 3950aa77f6cSMauro Carvalho Chehab 3960aa77f6cSMauro Carvalho Chehab /* image formats. */ 3970aa77f6cSMauro Carvalho Chehab /* JPEG formats must be defined last to support jpeg_enable parameter */ 3980aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt formats[] = { 3990aa77f6cSMauro Carvalho Chehab { 4000aa77f6cSMauro Carvalho Chehab .name = "4:2:2, packed, YUYV", 4010aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_YUYV, 4020aa77f6cSMauro Carvalho Chehab .depth = 16 4030aa77f6cSMauro Carvalho Chehab 4040aa77f6cSMauro Carvalho Chehab }, { 4050aa77f6cSMauro Carvalho Chehab .name = "4:2:2, packed, UYVY", 4060aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_UYVY, 4070aa77f6cSMauro Carvalho Chehab .depth = 16 4080aa77f6cSMauro Carvalho Chehab }, { 4095c632b22SHans Verkuil .name = "4:2:2, planar, YUV422P", 4105c632b22SHans Verkuil .fourcc = V4L2_PIX_FMT_YUV422P, 4115c632b22SHans Verkuil .depth = 16 4125c632b22SHans Verkuil 4135c632b22SHans Verkuil }, { 4140aa77f6cSMauro Carvalho Chehab .name = "8bpp GREY", 4150aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_GREY, 4160aa77f6cSMauro Carvalho Chehab .depth = 8 4170aa77f6cSMauro Carvalho Chehab }, { 4180aa77f6cSMauro Carvalho Chehab .name = "JPG", 4190aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_JPEG, 4200aa77f6cSMauro Carvalho Chehab .depth = 24 4210aa77f6cSMauro Carvalho Chehab }, { 4220aa77f6cSMauro Carvalho Chehab .name = "MJPG", 4230aa77f6cSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_MJPEG, 4240aa77f6cSMauro Carvalho Chehab .depth = 24 4250aa77f6cSMauro Carvalho Chehab } 4260aa77f6cSMauro Carvalho Chehab }; 4270aa77f6cSMauro Carvalho Chehab 4285e950fafSDean Anderson static int norm_maxw(struct s2255_vc *vc) 4290aa77f6cSMauro Carvalho Chehab { 4305e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4310aa77f6cSMauro Carvalho Chehab LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; 4320aa77f6cSMauro Carvalho Chehab } 4330aa77f6cSMauro Carvalho Chehab 4345e950fafSDean Anderson static int norm_maxh(struct s2255_vc *vc) 4350aa77f6cSMauro Carvalho Chehab { 4365e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4370aa77f6cSMauro Carvalho Chehab (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); 4380aa77f6cSMauro Carvalho Chehab } 4390aa77f6cSMauro Carvalho Chehab 4405e950fafSDean Anderson static int norm_minw(struct s2255_vc *vc) 4410aa77f6cSMauro Carvalho Chehab { 4425e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4430aa77f6cSMauro Carvalho Chehab LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; 4440aa77f6cSMauro Carvalho Chehab } 4450aa77f6cSMauro Carvalho Chehab 4465e950fafSDean Anderson static int norm_minh(struct s2255_vc *vc) 4470aa77f6cSMauro Carvalho Chehab { 4485e950fafSDean Anderson return (vc->std & V4L2_STD_525_60) ? 4490aa77f6cSMauro Carvalho Chehab (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); 4500aa77f6cSMauro Carvalho Chehab } 4510aa77f6cSMauro Carvalho Chehab 4520aa77f6cSMauro Carvalho Chehab 4530aa77f6cSMauro Carvalho Chehab /* 4540aa77f6cSMauro Carvalho Chehab * TODO: fixme: move YUV reordering to hardware 4550aa77f6cSMauro Carvalho Chehab * converts 2255 planar format to yuyv or uyvy 4560aa77f6cSMauro Carvalho Chehab */ 4570aa77f6cSMauro Carvalho Chehab static void planar422p_to_yuv_packed(const unsigned char *in, 4580aa77f6cSMauro Carvalho Chehab unsigned char *out, 4590aa77f6cSMauro Carvalho Chehab int width, int height, 4600aa77f6cSMauro Carvalho Chehab int fmt) 4610aa77f6cSMauro Carvalho Chehab { 4620aa77f6cSMauro Carvalho Chehab unsigned char *pY; 4630aa77f6cSMauro Carvalho Chehab unsigned char *pCb; 4640aa77f6cSMauro Carvalho Chehab unsigned char *pCr; 4650aa77f6cSMauro Carvalho Chehab unsigned long size = height * width; 4660aa77f6cSMauro Carvalho Chehab unsigned int i; 4670aa77f6cSMauro Carvalho Chehab pY = (unsigned char *)in; 4680aa77f6cSMauro Carvalho Chehab pCr = (unsigned char *)in + height * width; 4690aa77f6cSMauro Carvalho Chehab pCb = (unsigned char *)in + height * width + (height * width / 2); 4700aa77f6cSMauro Carvalho Chehab for (i = 0; i < size * 2; i += 4) { 4710aa77f6cSMauro Carvalho Chehab out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; 4720aa77f6cSMauro Carvalho Chehab out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; 4730aa77f6cSMauro Carvalho Chehab out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; 4740aa77f6cSMauro Carvalho Chehab out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; 4750aa77f6cSMauro Carvalho Chehab } 4760aa77f6cSMauro Carvalho Chehab return; 4770aa77f6cSMauro Carvalho Chehab } 4780aa77f6cSMauro Carvalho Chehab 4790aa77f6cSMauro Carvalho Chehab static void s2255_reset_dsppower(struct s2255_dev *dev) 4800aa77f6cSMauro Carvalho Chehab { 4810aa77f6cSMauro Carvalho Chehab s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1); 482f5402007Ssensoray-dev msleep(20); 4830aa77f6cSMauro Carvalho Chehab s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); 4840aa77f6cSMauro Carvalho Chehab msleep(600); 4850aa77f6cSMauro Carvalho Chehab s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); 4860aa77f6cSMauro Carvalho Chehab return; 4870aa77f6cSMauro Carvalho Chehab } 4880aa77f6cSMauro Carvalho Chehab 4890aa77f6cSMauro Carvalho Chehab /* kickstarts the firmware loading. from probe 4900aa77f6cSMauro Carvalho Chehab */ 4910aa77f6cSMauro Carvalho Chehab static void s2255_timer(unsigned long user_data) 4920aa77f6cSMauro Carvalho Chehab { 4930aa77f6cSMauro Carvalho Chehab struct s2255_fw *data = (struct s2255_fw *)user_data; 4940aa77f6cSMauro Carvalho Chehab if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { 495f5402007Ssensoray-dev pr_err("s2255: can't submit urb\n"); 4960aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 4970aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 4980aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 4990aa77f6cSMauro Carvalho Chehab return; 5000aa77f6cSMauro Carvalho Chehab } 5010aa77f6cSMauro Carvalho Chehab } 5020aa77f6cSMauro Carvalho Chehab 5030aa77f6cSMauro Carvalho Chehab 5040aa77f6cSMauro Carvalho Chehab /* this loads the firmware asynchronously. 5050b84caabSHans Verkuil Originally this was done synchronously in probe. 5060aa77f6cSMauro Carvalho Chehab But it is better to load it asynchronously here than block 5070aa77f6cSMauro Carvalho Chehab inside the probe function. Blocking inside probe affects boot time. 5080aa77f6cSMauro Carvalho Chehab FW loading is triggered by the timer in the probe function 5090aa77f6cSMauro Carvalho Chehab */ 5100aa77f6cSMauro Carvalho Chehab static void s2255_fwchunk_complete(struct urb *urb) 5110aa77f6cSMauro Carvalho Chehab { 5120aa77f6cSMauro Carvalho Chehab struct s2255_fw *data = urb->context; 5130aa77f6cSMauro Carvalho Chehab struct usb_device *udev = urb->dev; 5140aa77f6cSMauro Carvalho Chehab int len; 5150aa77f6cSMauro Carvalho Chehab if (urb->status) { 5160aa77f6cSMauro Carvalho Chehab dev_err(&udev->dev, "URB failed with status %d\n", urb->status); 5170aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 5180aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 5190aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 5200aa77f6cSMauro Carvalho Chehab return; 5210aa77f6cSMauro Carvalho Chehab } 5220aa77f6cSMauro Carvalho Chehab if (data->fw_urb == NULL) { 5230aa77f6cSMauro Carvalho Chehab s2255_dev_err(&udev->dev, "disconnected\n"); 5240aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 5250aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 5260aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 5270aa77f6cSMauro Carvalho Chehab return; 5280aa77f6cSMauro Carvalho Chehab } 5290aa77f6cSMauro Carvalho Chehab #define CHUNK_SIZE 512 5300aa77f6cSMauro Carvalho Chehab /* all USB transfers must be done with continuous kernel memory. 5310aa77f6cSMauro Carvalho Chehab can't allocate more than 128k in current linux kernel, so 5320aa77f6cSMauro Carvalho Chehab upload the firmware in chunks 5330aa77f6cSMauro Carvalho Chehab */ 5340aa77f6cSMauro Carvalho Chehab if (data->fw_loaded < data->fw_size) { 5350aa77f6cSMauro Carvalho Chehab len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? 5360aa77f6cSMauro Carvalho Chehab data->fw_size % CHUNK_SIZE : CHUNK_SIZE; 5370aa77f6cSMauro Carvalho Chehab 5380aa77f6cSMauro Carvalho Chehab if (len < CHUNK_SIZE) 5390aa77f6cSMauro Carvalho Chehab memset(data->pfw_data, 0, CHUNK_SIZE); 5400aa77f6cSMauro Carvalho Chehab 5410aa77f6cSMauro Carvalho Chehab memcpy(data->pfw_data, 5420aa77f6cSMauro Carvalho Chehab (char *) data->fw->data + data->fw_loaded, len); 5430aa77f6cSMauro Carvalho Chehab 5440aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), 5450aa77f6cSMauro Carvalho Chehab data->pfw_data, CHUNK_SIZE, 5460aa77f6cSMauro Carvalho Chehab s2255_fwchunk_complete, data); 5470aa77f6cSMauro Carvalho Chehab if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { 5480aa77f6cSMauro Carvalho Chehab dev_err(&udev->dev, "failed submit URB\n"); 5490aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_FAILED); 5500aa77f6cSMauro Carvalho Chehab /* wake up anything waiting for the firmware */ 5510aa77f6cSMauro Carvalho Chehab wake_up(&data->wait_fw); 5520aa77f6cSMauro Carvalho Chehab return; 5530aa77f6cSMauro Carvalho Chehab } 5540aa77f6cSMauro Carvalho Chehab data->fw_loaded += len; 555f5402007Ssensoray-dev } else 5560aa77f6cSMauro Carvalho Chehab atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); 5570aa77f6cSMauro Carvalho Chehab return; 5580aa77f6cSMauro Carvalho Chehab 5590aa77f6cSMauro Carvalho Chehab } 5600aa77f6cSMauro Carvalho Chehab 5615e950fafSDean Anderson static int s2255_got_frame(struct s2255_vc *vc, int jpgsize) 5620aa77f6cSMauro Carvalho Chehab { 5630aa77f6cSMauro Carvalho Chehab struct s2255_buffer *buf; 5645e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 5650aa77f6cSMauro Carvalho Chehab unsigned long flags = 0; 5660aa77f6cSMauro Carvalho Chehab int rc = 0; 567340a30c5Ssensoray-dev spin_lock_irqsave(&vc->qlock, flags); 5685e950fafSDean Anderson if (list_empty(&vc->buf_list)) { 569f5402007Ssensoray-dev dprintk(dev, 1, "No active queue to serve\n"); 5700aa77f6cSMauro Carvalho Chehab rc = -1; 5710aa77f6cSMauro Carvalho Chehab goto unlock; 5720aa77f6cSMauro Carvalho Chehab } 5735e950fafSDean Anderson buf = list_entry(vc->buf_list.next, 574340a30c5Ssensoray-dev struct s2255_buffer, list); 575340a30c5Ssensoray-dev list_del(&buf->list); 576340a30c5Ssensoray-dev v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); 5775e950fafSDean Anderson s2255_fillbuff(vc, buf, jpgsize); 578340a30c5Ssensoray-dev dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf); 5790aa77f6cSMauro Carvalho Chehab unlock: 580340a30c5Ssensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 5810aa77f6cSMauro Carvalho Chehab return rc; 5820aa77f6cSMauro Carvalho Chehab } 5830aa77f6cSMauro Carvalho Chehab 5840aa77f6cSMauro Carvalho Chehab static const struct s2255_fmt *format_by_fourcc(int fourcc) 5850aa77f6cSMauro Carvalho Chehab { 5860aa77f6cSMauro Carvalho Chehab unsigned int i; 5870aa77f6cSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(formats); i++) { 5880aa77f6cSMauro Carvalho Chehab if (-1 == formats[i].fourcc) 5890aa77f6cSMauro Carvalho Chehab continue; 5900aa77f6cSMauro Carvalho Chehab if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || 5910aa77f6cSMauro Carvalho Chehab (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) 5920aa77f6cSMauro Carvalho Chehab continue; 5930aa77f6cSMauro Carvalho Chehab if (formats[i].fourcc == fourcc) 5940aa77f6cSMauro Carvalho Chehab return formats + i; 5950aa77f6cSMauro Carvalho Chehab } 5960aa77f6cSMauro Carvalho Chehab return NULL; 5970aa77f6cSMauro Carvalho Chehab } 5980aa77f6cSMauro Carvalho Chehab 5990aa77f6cSMauro Carvalho Chehab /* video buffer vmalloc implementation based partly on VIVI driver which is 6000aa77f6cSMauro Carvalho Chehab * Copyright (c) 2006 by 6010aa77f6cSMauro Carvalho Chehab * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> 6020aa77f6cSMauro Carvalho Chehab * Ted Walther <ted--a.t--enumera.com> 6030aa77f6cSMauro Carvalho Chehab * John Sokol <sokol--a.t--videotechnology.com> 6040aa77f6cSMauro Carvalho Chehab * http://v4l.videotechnology.com/ 6050aa77f6cSMauro Carvalho Chehab * 6060aa77f6cSMauro Carvalho Chehab */ 6075e950fafSDean Anderson static void s2255_fillbuff(struct s2255_vc *vc, 6080aa77f6cSMauro Carvalho Chehab struct s2255_buffer *buf, int jpgsize) 6090aa77f6cSMauro Carvalho Chehab { 6100aa77f6cSMauro Carvalho Chehab int pos = 0; 6110aa77f6cSMauro Carvalho Chehab const char *tmpbuf; 612340a30c5Ssensoray-dev char *vbuf = vb2_plane_vaddr(&buf->vb, 0); 6130aa77f6cSMauro Carvalho Chehab unsigned long last_frame; 6145e950fafSDean Anderson struct s2255_dev *dev = vc->dev; 6150aa77f6cSMauro Carvalho Chehab 6160aa77f6cSMauro Carvalho Chehab if (!vbuf) 6170aa77f6cSMauro Carvalho Chehab return; 6185e950fafSDean Anderson last_frame = vc->last_frame; 6190aa77f6cSMauro Carvalho Chehab if (last_frame != -1) { 6200aa77f6cSMauro Carvalho Chehab tmpbuf = 6215e950fafSDean Anderson (const char *)vc->buffer.frame[last_frame].lpvbits; 6228bf405a0SDean Anderson switch (vc->fmt->fourcc) { 6230aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUYV: 6240aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_UYVY: 6250aa77f6cSMauro Carvalho Chehab planar422p_to_yuv_packed((const unsigned char *)tmpbuf, 626340a30c5Ssensoray-dev vbuf, vc->width, 627340a30c5Ssensoray-dev vc->height, 6288bf405a0SDean Anderson vc->fmt->fourcc); 6290aa77f6cSMauro Carvalho Chehab break; 6300aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_GREY: 631340a30c5Ssensoray-dev memcpy(vbuf, tmpbuf, vc->width * vc->height); 6320aa77f6cSMauro Carvalho Chehab break; 6330aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_JPEG: 6340aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_MJPEG: 635340a30c5Ssensoray-dev buf->vb.v4l2_buf.length = jpgsize; 636340a30c5Ssensoray-dev memcpy(vbuf, tmpbuf, jpgsize); 6370aa77f6cSMauro Carvalho Chehab break; 6380aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUV422P: 6390aa77f6cSMauro Carvalho Chehab memcpy(vbuf, tmpbuf, 640340a30c5Ssensoray-dev vc->width * vc->height * 2); 6410aa77f6cSMauro Carvalho Chehab break; 6420aa77f6cSMauro Carvalho Chehab default: 643f5402007Ssensoray-dev pr_info("s2255: unknown format?\n"); 6440aa77f6cSMauro Carvalho Chehab } 6455e950fafSDean Anderson vc->last_frame = -1; 6460aa77f6cSMauro Carvalho Chehab } else { 647f5402007Ssensoray-dev pr_err("s2255: =======no frame\n"); 6480aa77f6cSMauro Carvalho Chehab return; 6490aa77f6cSMauro Carvalho Chehab } 650f5402007Ssensoray-dev dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n", 6510aa77f6cSMauro Carvalho Chehab (unsigned long)vbuf, pos); 6520aa77f6cSMauro Carvalho Chehab /* tell v4l buffer was filled */ 653340a30c5Ssensoray-dev buf->vb.v4l2_buf.field = vc->field; 654340a30c5Ssensoray-dev buf->vb.v4l2_buf.sequence = vc->frame_count; 655340a30c5Ssensoray-dev v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); 656340a30c5Ssensoray-dev vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); 6570aa77f6cSMauro Carvalho Chehab } 6580aa77f6cSMauro Carvalho Chehab 6590aa77f6cSMauro Carvalho Chehab 6600aa77f6cSMauro Carvalho Chehab /* ------------------------------------------------------------------ 6610aa77f6cSMauro Carvalho Chehab Videobuf operations 6620aa77f6cSMauro Carvalho Chehab ------------------------------------------------------------------*/ 6630aa77f6cSMauro Carvalho Chehab 664340a30c5Ssensoray-dev static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, 665340a30c5Ssensoray-dev unsigned int *nbuffers, unsigned int *nplanes, 666340a30c5Ssensoray-dev unsigned int sizes[], void *alloc_ctxs[]) 6670aa77f6cSMauro Carvalho Chehab { 668340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vq); 6699da62eb0SDean Anderson if (*nbuffers < S2255_MIN_BUFS) 6709da62eb0SDean Anderson *nbuffers = S2255_MIN_BUFS; 671340a30c5Ssensoray-dev *nplanes = 1; 672340a30c5Ssensoray-dev sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3); 6730aa77f6cSMauro Carvalho Chehab return 0; 6740aa77f6cSMauro Carvalho Chehab } 6750aa77f6cSMauro Carvalho Chehab 676340a30c5Ssensoray-dev static int buffer_prepare(struct vb2_buffer *vb) 6770aa77f6cSMauro Carvalho Chehab { 678340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); 6790aa77f6cSMauro Carvalho Chehab struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); 6805e950fafSDean Anderson int w = vc->width; 6815e950fafSDean Anderson int h = vc->height; 682340a30c5Ssensoray-dev unsigned long size; 683340a30c5Ssensoray-dev 684340a30c5Ssensoray-dev dprintk(vc->dev, 4, "%s\n", __func__); 6855e950fafSDean Anderson if (vc->fmt == NULL) 6860aa77f6cSMauro Carvalho Chehab return -EINVAL; 6870aa77f6cSMauro Carvalho Chehab 6885e950fafSDean Anderson if ((w < norm_minw(vc)) || 6895e950fafSDean Anderson (w > norm_maxw(vc)) || 6905e950fafSDean Anderson (h < norm_minh(vc)) || 6915e950fafSDean Anderson (h > norm_maxh(vc))) { 69292cde477SDean Anderson dprintk(vc->dev, 4, "invalid buffer prepare\n"); 6930aa77f6cSMauro Carvalho Chehab return -EINVAL; 6940aa77f6cSMauro Carvalho Chehab } 695340a30c5Ssensoray-dev size = w * h * (vc->fmt->depth >> 3); 696340a30c5Ssensoray-dev if (vb2_plane_size(vb, 0) < size) { 69792cde477SDean Anderson dprintk(vc->dev, 4, "invalid buffer prepare\n"); 6980aa77f6cSMauro Carvalho Chehab return -EINVAL; 6990aa77f6cSMauro Carvalho Chehab } 7000aa77f6cSMauro Carvalho Chehab 701340a30c5Ssensoray-dev vb2_set_plane_payload(&buf->vb, 0, size); 7020aa77f6cSMauro Carvalho Chehab return 0; 7030aa77f6cSMauro Carvalho Chehab } 7040aa77f6cSMauro Carvalho Chehab 705340a30c5Ssensoray-dev static void buffer_queue(struct vb2_buffer *vb) 7060aa77f6cSMauro Carvalho Chehab { 7070aa77f6cSMauro Carvalho Chehab struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); 708340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); 709340a30c5Ssensoray-dev unsigned long flags = 0; 71092cde477SDean Anderson dprintk(vc->dev, 1, "%s\n", __func__); 711340a30c5Ssensoray-dev spin_lock_irqsave(&vc->qlock, flags); 712340a30c5Ssensoray-dev list_add_tail(&buf->list, &vc->buf_list); 713340a30c5Ssensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 7140aa77f6cSMauro Carvalho Chehab } 7150aa77f6cSMauro Carvalho Chehab 716340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count); 717340a30c5Ssensoray-dev static int stop_streaming(struct vb2_queue *vq); 7180aa77f6cSMauro Carvalho Chehab 719340a30c5Ssensoray-dev static struct vb2_ops s2255_video_qops = { 720340a30c5Ssensoray-dev .queue_setup = queue_setup, 7210aa77f6cSMauro Carvalho Chehab .buf_prepare = buffer_prepare, 7220aa77f6cSMauro Carvalho Chehab .buf_queue = buffer_queue, 723340a30c5Ssensoray-dev .start_streaming = start_streaming, 724340a30c5Ssensoray-dev .stop_streaming = stop_streaming, 725340a30c5Ssensoray-dev .wait_prepare = vb2_ops_wait_prepare, 726340a30c5Ssensoray-dev .wait_finish = vb2_ops_wait_finish, 7270aa77f6cSMauro Carvalho Chehab }; 7280aa77f6cSMauro Carvalho Chehab 7290aa77f6cSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void *priv, 7300aa77f6cSMauro Carvalho Chehab struct v4l2_capability *cap) 7310aa77f6cSMauro Carvalho Chehab { 732340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 733340a30c5Ssensoray-dev struct s2255_dev *dev = vc->dev; 73439696009SHans Verkuil 7350aa77f6cSMauro Carvalho Chehab strlcpy(cap->driver, "s2255", sizeof(cap->driver)); 7360aa77f6cSMauro Carvalho Chehab strlcpy(cap->card, "s2255", sizeof(cap->card)); 7370aa77f6cSMauro Carvalho Chehab usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 738340a30c5Ssensoray-dev cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 739340a30c5Ssensoray-dev V4L2_CAP_READWRITE; 74039696009SHans Verkuil cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; 7410aa77f6cSMauro Carvalho Chehab return 0; 7420aa77f6cSMauro Carvalho Chehab } 7430aa77f6cSMauro Carvalho Chehab 7440aa77f6cSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 7450aa77f6cSMauro Carvalho Chehab struct v4l2_fmtdesc *f) 7460aa77f6cSMauro Carvalho Chehab { 7470aa77f6cSMauro Carvalho Chehab int index = f->index; 7480aa77f6cSMauro Carvalho Chehab 7490aa77f6cSMauro Carvalho Chehab if (index >= ARRAY_SIZE(formats)) 7500aa77f6cSMauro Carvalho Chehab return -EINVAL; 7510aa77f6cSMauro Carvalho Chehab if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || 7520aa77f6cSMauro Carvalho Chehab (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) 7530aa77f6cSMauro Carvalho Chehab return -EINVAL; 7540aa77f6cSMauro Carvalho Chehab strlcpy(f->description, formats[index].name, sizeof(f->description)); 7550aa77f6cSMauro Carvalho Chehab f->pixelformat = formats[index].fourcc; 7560aa77f6cSMauro Carvalho Chehab return 0; 7570aa77f6cSMauro Carvalho Chehab } 7580aa77f6cSMauro Carvalho Chehab 7590aa77f6cSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 7600aa77f6cSMauro Carvalho Chehab struct v4l2_format *f) 7610aa77f6cSMauro Carvalho Chehab { 762340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 7635e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 7640aa77f6cSMauro Carvalho Chehab 7655e950fafSDean Anderson f->fmt.pix.width = vc->width; 7665e950fafSDean Anderson f->fmt.pix.height = vc->height; 76792513611SHans Verkuil if (f->fmt.pix.height >= 76892513611SHans Verkuil (is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2) 76992513611SHans Verkuil f->fmt.pix.field = V4L2_FIELD_INTERLACED; 77092513611SHans Verkuil else 77192513611SHans Verkuil f->fmt.pix.field = V4L2_FIELD_TOP; 7725e950fafSDean Anderson f->fmt.pix.pixelformat = vc->fmt->fourcc; 7735e950fafSDean Anderson f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3); 7740aa77f6cSMauro Carvalho Chehab f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 77529ceb110SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 77629ceb110SHans Verkuil f->fmt.pix.priv = 0; 7770aa77f6cSMauro Carvalho Chehab return 0; 7780aa77f6cSMauro Carvalho Chehab } 7790aa77f6cSMauro Carvalho Chehab 7800aa77f6cSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 7810aa77f6cSMauro Carvalho Chehab struct v4l2_format *f) 7820aa77f6cSMauro Carvalho Chehab { 7830aa77f6cSMauro Carvalho Chehab const struct s2255_fmt *fmt; 7840aa77f6cSMauro Carvalho Chehab enum v4l2_field field; 785340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 7865e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 7870aa77f6cSMauro Carvalho Chehab 7880aa77f6cSMauro Carvalho Chehab fmt = format_by_fourcc(f->fmt.pix.pixelformat); 7890aa77f6cSMauro Carvalho Chehab 7900aa77f6cSMauro Carvalho Chehab if (fmt == NULL) 7910aa77f6cSMauro Carvalho Chehab return -EINVAL; 7920aa77f6cSMauro Carvalho Chehab 7930aa77f6cSMauro Carvalho Chehab field = f->fmt.pix.field; 7940aa77f6cSMauro Carvalho Chehab 79592cde477SDean Anderson dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n", 7960aa77f6cSMauro Carvalho Chehab __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); 7970aa77f6cSMauro Carvalho Chehab if (is_ntsc) { 7980aa77f6cSMauro Carvalho Chehab /* NTSC */ 7990aa77f6cSMauro Carvalho Chehab if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { 8000aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; 80192513611SHans Verkuil field = V4L2_FIELD_INTERLACED; 8020aa77f6cSMauro Carvalho Chehab } else { 8030aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; 8040aa77f6cSMauro Carvalho Chehab field = V4L2_FIELD_TOP; 8050aa77f6cSMauro Carvalho Chehab } 8060aa77f6cSMauro Carvalho Chehab if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) 8070aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; 8080aa77f6cSMauro Carvalho Chehab else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) 8090aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; 8100aa77f6cSMauro Carvalho Chehab else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) 8110aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; 8120aa77f6cSMauro Carvalho Chehab else 8130aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; 8140aa77f6cSMauro Carvalho Chehab } else { 8150aa77f6cSMauro Carvalho Chehab /* PAL */ 8160aa77f6cSMauro Carvalho Chehab if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { 8170aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; 81892513611SHans Verkuil field = V4L2_FIELD_INTERLACED; 8190aa77f6cSMauro Carvalho Chehab } else { 8200aa77f6cSMauro Carvalho Chehab f->fmt.pix.height = NUM_LINES_1CIFS_PAL; 8210aa77f6cSMauro Carvalho Chehab field = V4L2_FIELD_TOP; 8220aa77f6cSMauro Carvalho Chehab } 82392513611SHans Verkuil if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) 8240aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_4CIFS_PAL; 82592513611SHans Verkuil else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) 8260aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_2CIFS_PAL; 82792513611SHans Verkuil else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) 8280aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_1CIFS_PAL; 82992513611SHans Verkuil else 8300aa77f6cSMauro Carvalho Chehab f->fmt.pix.width = LINE_SZ_1CIFS_PAL; 8310aa77f6cSMauro Carvalho Chehab } 8320aa77f6cSMauro Carvalho Chehab f->fmt.pix.field = field; 8330aa77f6cSMauro Carvalho Chehab f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 8340aa77f6cSMauro Carvalho Chehab f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 83529ceb110SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 83629ceb110SHans Verkuil f->fmt.pix.priv = 0; 83792cde477SDean Anderson dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__, 8380aa77f6cSMauro Carvalho Chehab f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); 8390aa77f6cSMauro Carvalho Chehab return 0; 8400aa77f6cSMauro Carvalho Chehab } 8410aa77f6cSMauro Carvalho Chehab 8420aa77f6cSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 8430aa77f6cSMauro Carvalho Chehab struct v4l2_format *f) 8440aa77f6cSMauro Carvalho Chehab { 845340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 8460aa77f6cSMauro Carvalho Chehab const struct s2255_fmt *fmt; 847340a30c5Ssensoray-dev struct vb2_queue *q = &vc->vb_vidq; 8480aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 8490aa77f6cSMauro Carvalho Chehab int ret; 8500aa77f6cSMauro Carvalho Chehab 851340a30c5Ssensoray-dev ret = vidioc_try_fmt_vid_cap(file, vc, f); 8520aa77f6cSMauro Carvalho Chehab 8530aa77f6cSMauro Carvalho Chehab if (ret < 0) 8540aa77f6cSMauro Carvalho Chehab return ret; 8550aa77f6cSMauro Carvalho Chehab 8560aa77f6cSMauro Carvalho Chehab fmt = format_by_fourcc(f->fmt.pix.pixelformat); 8570aa77f6cSMauro Carvalho Chehab 8580aa77f6cSMauro Carvalho Chehab if (fmt == NULL) 8590aa77f6cSMauro Carvalho Chehab return -EINVAL; 8600aa77f6cSMauro Carvalho Chehab 861340a30c5Ssensoray-dev if (vb2_is_busy(q)) { 86292cde477SDean Anderson dprintk(vc->dev, 1, "queue busy\n"); 863340a30c5Ssensoray-dev return -EBUSY; 8640aa77f6cSMauro Carvalho Chehab } 8650aa77f6cSMauro Carvalho Chehab 8665e950fafSDean Anderson mode = vc->mode; 8675e950fafSDean Anderson vc->fmt = fmt; 8685e950fafSDean Anderson vc->width = f->fmt.pix.width; 8695e950fafSDean Anderson vc->height = f->fmt.pix.height; 870340a30c5Ssensoray-dev vc->field = f->fmt.pix.field; 8715e950fafSDean Anderson if (vc->width > norm_minw(vc)) { 8725e950fafSDean Anderson if (vc->height > norm_minh(vc)) { 8735e950fafSDean Anderson if (vc->cap_parm.capturemode & 8740aa77f6cSMauro Carvalho Chehab V4L2_MODE_HIGHQUALITY) 8750aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_4CIFSI; 8760aa77f6cSMauro Carvalho Chehab else 8770aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_4CIFS; 8780aa77f6cSMauro Carvalho Chehab } else 8790aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_2CIFS; 8800aa77f6cSMauro Carvalho Chehab 8810aa77f6cSMauro Carvalho Chehab } else { 8820aa77f6cSMauro Carvalho Chehab mode.scale = SCALE_1CIFS; 8830aa77f6cSMauro Carvalho Chehab } 8840aa77f6cSMauro Carvalho Chehab /* color mode */ 8855e950fafSDean Anderson switch (vc->fmt->fourcc) { 8860aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_GREY: 8870aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8880aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_Y8; 8890aa77f6cSMauro Carvalho Chehab break; 8900aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_JPEG: 8910aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_MJPEG: 8920aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8930aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_JPG; 8945e950fafSDean Anderson mode.color |= (vc->jpegqual << 8); 8950aa77f6cSMauro Carvalho Chehab break; 8960aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUV422P: 8970aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 8980aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_YUVPL; 8990aa77f6cSMauro Carvalho Chehab break; 9000aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_YUYV: 9010aa77f6cSMauro Carvalho Chehab case V4L2_PIX_FMT_UYVY: 9020aa77f6cSMauro Carvalho Chehab default: 9030aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_COLOR; 9040aa77f6cSMauro Carvalho Chehab mode.color |= COLOR_YUVPK; 9050aa77f6cSMauro Carvalho Chehab break; 9060aa77f6cSMauro Carvalho Chehab } 9075e950fafSDean Anderson if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR)) 9080aa77f6cSMauro Carvalho Chehab mode.restart = 1; 9095e950fafSDean Anderson else if (mode.scale != vc->mode.scale) 9100aa77f6cSMauro Carvalho Chehab mode.restart = 1; 9115e950fafSDean Anderson else if (mode.format != vc->mode.format) 9120aa77f6cSMauro Carvalho Chehab mode.restart = 1; 9135e950fafSDean Anderson vc->mode = mode; 9145e950fafSDean Anderson (void) s2255_set_mode(vc, &mode); 915340a30c5Ssensoray-dev return 0; 9160aa77f6cSMauro Carvalho Chehab } 9170aa77f6cSMauro Carvalho Chehab 9180aa77f6cSMauro Carvalho Chehab 9190aa77f6cSMauro Carvalho Chehab /* write to the configuration pipe, synchronously */ 9200aa77f6cSMauro Carvalho Chehab static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, 9210aa77f6cSMauro Carvalho Chehab int size) 9220aa77f6cSMauro Carvalho Chehab { 9230aa77f6cSMauro Carvalho Chehab int pipe; 9240aa77f6cSMauro Carvalho Chehab int done; 9250aa77f6cSMauro Carvalho Chehab long retval = -1; 9260aa77f6cSMauro Carvalho Chehab if (udev) { 9270aa77f6cSMauro Carvalho Chehab pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); 9280aa77f6cSMauro Carvalho Chehab retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); 9290aa77f6cSMauro Carvalho Chehab } 9300aa77f6cSMauro Carvalho Chehab return retval; 9310aa77f6cSMauro Carvalho Chehab } 9320aa77f6cSMauro Carvalho Chehab 9330aa77f6cSMauro Carvalho Chehab static u32 get_transfer_size(struct s2255_mode *mode) 9340aa77f6cSMauro Carvalho Chehab { 9350aa77f6cSMauro Carvalho Chehab int linesPerFrame = LINE_SZ_DEF; 9360aa77f6cSMauro Carvalho Chehab int pixelsPerLine = NUM_LINES_DEF; 9370aa77f6cSMauro Carvalho Chehab u32 outImageSize; 9380aa77f6cSMauro Carvalho Chehab u32 usbInSize; 9390aa77f6cSMauro Carvalho Chehab unsigned int mask_mult; 9400aa77f6cSMauro Carvalho Chehab 9410aa77f6cSMauro Carvalho Chehab if (mode == NULL) 9420aa77f6cSMauro Carvalho Chehab return 0; 9430aa77f6cSMauro Carvalho Chehab 9440aa77f6cSMauro Carvalho Chehab if (mode->format == FORMAT_NTSC) { 9450aa77f6cSMauro Carvalho Chehab switch (mode->scale) { 9460aa77f6cSMauro Carvalho Chehab case SCALE_4CIFS: 9470aa77f6cSMauro Carvalho Chehab case SCALE_4CIFSI: 9480aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; 9490aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_4CIFS_NTSC; 9500aa77f6cSMauro Carvalho Chehab break; 9510aa77f6cSMauro Carvalho Chehab case SCALE_2CIFS: 9520aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_2CIFS_NTSC; 9530aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_2CIFS_NTSC; 9540aa77f6cSMauro Carvalho Chehab break; 9550aa77f6cSMauro Carvalho Chehab case SCALE_1CIFS: 9560aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_1CIFS_NTSC; 9570aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_1CIFS_NTSC; 9580aa77f6cSMauro Carvalho Chehab break; 9590aa77f6cSMauro Carvalho Chehab default: 9600aa77f6cSMauro Carvalho Chehab break; 9610aa77f6cSMauro Carvalho Chehab } 9620aa77f6cSMauro Carvalho Chehab } else if (mode->format == FORMAT_PAL) { 9630aa77f6cSMauro Carvalho Chehab switch (mode->scale) { 9640aa77f6cSMauro Carvalho Chehab case SCALE_4CIFS: 9650aa77f6cSMauro Carvalho Chehab case SCALE_4CIFSI: 9660aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_4CIFS_PAL * 2; 9670aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_4CIFS_PAL; 9680aa77f6cSMauro Carvalho Chehab break; 9690aa77f6cSMauro Carvalho Chehab case SCALE_2CIFS: 9700aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_2CIFS_PAL; 9710aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_2CIFS_PAL; 9720aa77f6cSMauro Carvalho Chehab break; 9730aa77f6cSMauro Carvalho Chehab case SCALE_1CIFS: 9740aa77f6cSMauro Carvalho Chehab linesPerFrame = NUM_LINES_1CIFS_PAL; 9750aa77f6cSMauro Carvalho Chehab pixelsPerLine = LINE_SZ_1CIFS_PAL; 9760aa77f6cSMauro Carvalho Chehab break; 9770aa77f6cSMauro Carvalho Chehab default: 9780aa77f6cSMauro Carvalho Chehab break; 9790aa77f6cSMauro Carvalho Chehab } 9800aa77f6cSMauro Carvalho Chehab } 9810aa77f6cSMauro Carvalho Chehab outImageSize = linesPerFrame * pixelsPerLine; 9820aa77f6cSMauro Carvalho Chehab if ((mode->color & MASK_COLOR) != COLOR_Y8) { 9830aa77f6cSMauro Carvalho Chehab /* 2 bytes/pixel if not monochrome */ 9840aa77f6cSMauro Carvalho Chehab outImageSize *= 2; 9850aa77f6cSMauro Carvalho Chehab } 9860aa77f6cSMauro Carvalho Chehab 9870aa77f6cSMauro Carvalho Chehab /* total bytes to send including prefix and 4K padding; 9880aa77f6cSMauro Carvalho Chehab must be a multiple of USB_READ_SIZE */ 9890aa77f6cSMauro Carvalho Chehab usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ 9900aa77f6cSMauro Carvalho Chehab mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; 9910aa77f6cSMauro Carvalho Chehab /* if size not a multiple of USB_READ_SIZE */ 9920aa77f6cSMauro Carvalho Chehab if (usbInSize & ~mask_mult) 9930aa77f6cSMauro Carvalho Chehab usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); 9940aa77f6cSMauro Carvalho Chehab return usbInSize; 9950aa77f6cSMauro Carvalho Chehab } 9960aa77f6cSMauro Carvalho Chehab 9970aa77f6cSMauro Carvalho Chehab static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) 9980aa77f6cSMauro Carvalho Chehab { 9990aa77f6cSMauro Carvalho Chehab struct device *dev = &sdev->udev->dev; 10000aa77f6cSMauro Carvalho Chehab dev_info(dev, "------------------------------------------------\n"); 10010aa77f6cSMauro Carvalho Chehab dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); 10020aa77f6cSMauro Carvalho Chehab dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); 10030aa77f6cSMauro Carvalho Chehab dev_info(dev, "bright: 0x%x\n", mode->bright); 10040aa77f6cSMauro Carvalho Chehab dev_info(dev, "------------------------------------------------\n"); 10050aa77f6cSMauro Carvalho Chehab } 10060aa77f6cSMauro Carvalho Chehab 10070aa77f6cSMauro Carvalho Chehab /* 10080aa77f6cSMauro Carvalho Chehab * set mode is the function which controls the DSP. 10090aa77f6cSMauro Carvalho Chehab * the restart parameter in struct s2255_mode should be set whenever 10100aa77f6cSMauro Carvalho Chehab * the image size could change via color format, video system or image 10110aa77f6cSMauro Carvalho Chehab * size. 10120aa77f6cSMauro Carvalho Chehab * When the restart parameter is set, we sleep for ONE frame to allow the 10130aa77f6cSMauro Carvalho Chehab * DSP time to get the new frame 10140aa77f6cSMauro Carvalho Chehab */ 10155e950fafSDean Anderson static int s2255_set_mode(struct s2255_vc *vc, 10160aa77f6cSMauro Carvalho Chehab struct s2255_mode *mode) 10170aa77f6cSMauro Carvalho Chehab { 10180aa77f6cSMauro Carvalho Chehab int res; 10190aa77f6cSMauro Carvalho Chehab unsigned long chn_rev; 10205e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 10210b84caabSHans Verkuil int i; 102247d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 10230b84caabSHans Verkuil 102447d8c881SDean Anderson mutex_lock(&dev->cmdlock); 10255e950fafSDean Anderson chn_rev = G_chnmap[vc->idx]; 10265e950fafSDean Anderson dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx); 10270aa77f6cSMauro Carvalho Chehab /* if JPEG, set the quality */ 10280aa77f6cSMauro Carvalho Chehab if ((mode->color & MASK_COLOR) == COLOR_JPG) { 10290aa77f6cSMauro Carvalho Chehab mode->color &= ~MASK_COLOR; 10300aa77f6cSMauro Carvalho Chehab mode->color |= COLOR_JPG; 10310aa77f6cSMauro Carvalho Chehab mode->color &= ~MASK_JPG_QUALITY; 10325e950fafSDean Anderson mode->color |= (vc->jpegqual << 8); 10330aa77f6cSMauro Carvalho Chehab } 10340aa77f6cSMauro Carvalho Chehab /* save the mode */ 10355e950fafSDean Anderson vc->mode = *mode; 10365e950fafSDean Anderson vc->req_image_size = get_transfer_size(mode); 10375e950fafSDean Anderson dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size); 10380aa77f6cSMauro Carvalho Chehab /* set the mode */ 10390aa77f6cSMauro Carvalho Chehab buffer[0] = IN_DATA_TOKEN; 10400aa77f6cSMauro Carvalho Chehab buffer[1] = (__le32) cpu_to_le32(chn_rev); 10410aa77f6cSMauro Carvalho Chehab buffer[2] = CMD_SET_MODE; 10420b84caabSHans Verkuil for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++) 10435e950fafSDean Anderson buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]); 10445e950fafSDean Anderson vc->setmode_ready = 0; 10450aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 10460aa77f6cSMauro Carvalho Chehab if (debug) 10470aa77f6cSMauro Carvalho Chehab s2255_print_cfg(dev, mode); 10480aa77f6cSMauro Carvalho Chehab /* wait at least 3 frames before continuing */ 10490aa77f6cSMauro Carvalho Chehab if (mode->restart) { 10505e950fafSDean Anderson wait_event_timeout(vc->wait_setmode, 10515e950fafSDean Anderson (vc->setmode_ready != 0), 10520aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); 10535e950fafSDean Anderson if (vc->setmode_ready != 1) { 1054f5402007Ssensoray-dev dprintk(dev, 0, "s2255: no set mode response\n"); 10550aa77f6cSMauro Carvalho Chehab res = -EFAULT; 10560aa77f6cSMauro Carvalho Chehab } 10570aa77f6cSMauro Carvalho Chehab } 10580aa77f6cSMauro Carvalho Chehab /* clear the restart flag */ 10595e950fafSDean Anderson vc->mode.restart = 0; 10605e950fafSDean Anderson dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res); 106147d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 10620aa77f6cSMauro Carvalho Chehab return res; 10630aa77f6cSMauro Carvalho Chehab } 10640aa77f6cSMauro Carvalho Chehab 10655e950fafSDean Anderson static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus) 10660aa77f6cSMauro Carvalho Chehab { 10670aa77f6cSMauro Carvalho Chehab int res; 10680aa77f6cSMauro Carvalho Chehab u32 chn_rev; 10695e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 107047d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 107147d8c881SDean Anderson 107247d8c881SDean Anderson mutex_lock(&dev->cmdlock); 10735e950fafSDean Anderson chn_rev = G_chnmap[vc->idx]; 10745e950fafSDean Anderson dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx); 10750aa77f6cSMauro Carvalho Chehab /* form the get vid status command */ 10760aa77f6cSMauro Carvalho Chehab buffer[0] = IN_DATA_TOKEN; 10770aa77f6cSMauro Carvalho Chehab buffer[1] = (__le32) cpu_to_le32(chn_rev); 10780aa77f6cSMauro Carvalho Chehab buffer[2] = CMD_STATUS; 10790aa77f6cSMauro Carvalho Chehab *pstatus = 0; 10805e950fafSDean Anderson vc->vidstatus_ready = 0; 10810aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 10825e950fafSDean Anderson wait_event_timeout(vc->wait_vidstatus, 10835e950fafSDean Anderson (vc->vidstatus_ready != 0), 10840aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); 10855e950fafSDean Anderson if (vc->vidstatus_ready != 1) { 1086f5402007Ssensoray-dev dprintk(dev, 0, "s2255: no vidstatus response\n"); 10870aa77f6cSMauro Carvalho Chehab res = -EFAULT; 10880aa77f6cSMauro Carvalho Chehab } 10895e950fafSDean Anderson *pstatus = vc->vidstatus; 1090f5402007Ssensoray-dev dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus); 109147d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 10920aa77f6cSMauro Carvalho Chehab return res; 10930aa77f6cSMauro Carvalho Chehab } 10940aa77f6cSMauro Carvalho Chehab 1095340a30c5Ssensoray-dev static int start_streaming(struct vb2_queue *vq, unsigned int count) 10960aa77f6cSMauro Carvalho Chehab { 1097340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vq); 10980aa77f6cSMauro Carvalho Chehab int j; 109992cde477SDean Anderson 11005e950fafSDean Anderson vc->last_frame = -1; 11015e950fafSDean Anderson vc->bad_payload = 0; 11025e950fafSDean Anderson vc->cur_frame = 0; 11035e950fafSDean Anderson vc->frame_count = 0; 11040aa77f6cSMauro Carvalho Chehab for (j = 0; j < SYS_FRAMES; j++) { 11055e950fafSDean Anderson vc->buffer.frame[j].ulState = S2255_READ_IDLE; 11065e950fafSDean Anderson vc->buffer.frame[j].cur_size = 0; 11070aa77f6cSMauro Carvalho Chehab } 1108340a30c5Ssensoray-dev return s2255_start_acquire(vc); 11090aa77f6cSMauro Carvalho Chehab } 11100aa77f6cSMauro Carvalho Chehab 1111340a30c5Ssensoray-dev /* abort streaming and wait for last buffer */ 1112340a30c5Ssensoray-dev static int stop_streaming(struct vb2_queue *vq) 11130aa77f6cSMauro Carvalho Chehab { 1114340a30c5Ssensoray-dev struct s2255_vc *vc = vb2_get_drv_priv(vq); 1115340a30c5Ssensoray-dev struct s2255_buffer *buf, *node; 1116340a30c5Ssensoray-dev unsigned long flags; 1117340a30c5Ssensoray-dev (void) s2255_stop_acquire(vc); 1118340a30c5Ssensoray-dev spin_lock_irqsave(&vc->qlock, flags); 1119340a30c5Ssensoray-dev list_for_each_entry_safe(buf, node, &vc->buf_list, list) { 1120340a30c5Ssensoray-dev list_del(&buf->list); 1121340a30c5Ssensoray-dev vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); 1122340a30c5Ssensoray-dev dprintk(vc->dev, 2, "[%p/%d] done\n", 1123340a30c5Ssensoray-dev buf, buf->vb.v4l2_buf.index); 1124340a30c5Ssensoray-dev } 1125340a30c5Ssensoray-dev spin_unlock_irqrestore(&vc->qlock, flags); 11260aa77f6cSMauro Carvalho Chehab return 0; 11270aa77f6cSMauro Carvalho Chehab } 11280aa77f6cSMauro Carvalho Chehab 1129314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) 11300aa77f6cSMauro Carvalho Chehab { 1131340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 11320aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 1133340a30c5Ssensoray-dev struct vb2_queue *q = &vc->vb_vidq; 1134469af77aSHans Verkuil 1135340a30c5Ssensoray-dev /* 1136340a30c5Ssensoray-dev * Changing the standard implies a format change, which is not allowed 1137340a30c5Ssensoray-dev * while buffers for use with streaming have already been allocated. 1138340a30c5Ssensoray-dev */ 1139340a30c5Ssensoray-dev if (vb2_is_busy(q)) 1140340a30c5Ssensoray-dev return -EBUSY; 1141340a30c5Ssensoray-dev 114292cde477SDean Anderson mode = vc->mode; 1143314527acSHans Verkuil if (i & V4L2_STD_525_60) { 114492cde477SDean Anderson dprintk(vc->dev, 4, "%s 60 Hz\n", __func__); 11450aa77f6cSMauro Carvalho Chehab /* if changing format, reset frame decimation/intervals */ 11460aa77f6cSMauro Carvalho Chehab if (mode.format != FORMAT_NTSC) { 11470aa77f6cSMauro Carvalho Chehab mode.restart = 1; 11480aa77f6cSMauro Carvalho Chehab mode.format = FORMAT_NTSC; 11490aa77f6cSMauro Carvalho Chehab mode.fdec = FDEC_1; 11505e950fafSDean Anderson vc->width = LINE_SZ_4CIFS_NTSC; 11515e950fafSDean Anderson vc->height = NUM_LINES_4CIFS_NTSC * 2; 11520aa77f6cSMauro Carvalho Chehab } 1153314527acSHans Verkuil } else if (i & V4L2_STD_625_50) { 115492cde477SDean Anderson dprintk(vc->dev, 4, "%s 50 Hz\n", __func__); 11550aa77f6cSMauro Carvalho Chehab if (mode.format != FORMAT_PAL) { 11560aa77f6cSMauro Carvalho Chehab mode.restart = 1; 11570aa77f6cSMauro Carvalho Chehab mode.format = FORMAT_PAL; 11580aa77f6cSMauro Carvalho Chehab mode.fdec = FDEC_1; 11595e950fafSDean Anderson vc->width = LINE_SZ_4CIFS_PAL; 11605e950fafSDean Anderson vc->height = NUM_LINES_4CIFS_PAL * 2; 11610aa77f6cSMauro Carvalho Chehab } 1162340a30c5Ssensoray-dev } else 1163340a30c5Ssensoray-dev return -EINVAL; 116492cde477SDean Anderson vc->std = i; 11650aa77f6cSMauro Carvalho Chehab if (mode.restart) 116692cde477SDean Anderson s2255_set_mode(vc, &mode); 1167340a30c5Ssensoray-dev return 0; 11680aa77f6cSMauro Carvalho Chehab } 11690aa77f6cSMauro Carvalho Chehab 1170469af77aSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i) 1171469af77aSHans Verkuil { 1172340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 1173469af77aSHans Verkuil 117492cde477SDean Anderson *i = vc->std; 1175469af77aSHans Verkuil return 0; 1176469af77aSHans Verkuil } 1177469af77aSHans Verkuil 11780aa77f6cSMauro Carvalho Chehab /* Sensoray 2255 is a multiple channel capture device. 11790aa77f6cSMauro Carvalho Chehab It does not have a "crossbar" of inputs. 11800aa77f6cSMauro Carvalho Chehab We use one V4L device per channel. The user must 11810aa77f6cSMauro Carvalho Chehab be aware that certain combinations are not allowed. 11820aa77f6cSMauro Carvalho Chehab For instance, you cannot do full FPS on more than 2 channels(2 videodevs) 11830aa77f6cSMauro Carvalho Chehab at once in color(you can do full fps on 4 channels with greyscale. 11840aa77f6cSMauro Carvalho Chehab */ 11850aa77f6cSMauro Carvalho Chehab static int vidioc_enum_input(struct file *file, void *priv, 11860aa77f6cSMauro Carvalho Chehab struct v4l2_input *inp) 11870aa77f6cSMauro Carvalho Chehab { 1188340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 118992cde477SDean Anderson struct s2255_dev *dev = vc->dev; 11900aa77f6cSMauro Carvalho Chehab u32 status = 0; 119192cde477SDean Anderson 11920aa77f6cSMauro Carvalho Chehab if (inp->index != 0) 11930aa77f6cSMauro Carvalho Chehab return -EINVAL; 11940aa77f6cSMauro Carvalho Chehab inp->type = V4L2_INPUT_TYPE_CAMERA; 11950aa77f6cSMauro Carvalho Chehab inp->std = S2255_NORMS; 11960aa77f6cSMauro Carvalho Chehab inp->status = 0; 11970aa77f6cSMauro Carvalho Chehab if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { 11980aa77f6cSMauro Carvalho Chehab int rc; 119992cde477SDean Anderson rc = s2255_cmd_status(vc, &status); 1200f5402007Ssensoray-dev dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n", 1201f5402007Ssensoray-dev rc, status); 12020aa77f6cSMauro Carvalho Chehab if (rc == 0) 12030aa77f6cSMauro Carvalho Chehab inp->status = (status & 0x01) ? 0 12040aa77f6cSMauro Carvalho Chehab : V4L2_IN_ST_NO_SIGNAL; 12050aa77f6cSMauro Carvalho Chehab } 12060aa77f6cSMauro Carvalho Chehab switch (dev->pid) { 12070aa77f6cSMauro Carvalho Chehab case 0x2255: 12080aa77f6cSMauro Carvalho Chehab default: 12090aa77f6cSMauro Carvalho Chehab strlcpy(inp->name, "Composite", sizeof(inp->name)); 12100aa77f6cSMauro Carvalho Chehab break; 12110aa77f6cSMauro Carvalho Chehab case 0x2257: 12125e950fafSDean Anderson strlcpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video", 12130aa77f6cSMauro Carvalho Chehab sizeof(inp->name)); 12140aa77f6cSMauro Carvalho Chehab break; 12150aa77f6cSMauro Carvalho Chehab } 12160aa77f6cSMauro Carvalho Chehab return 0; 12170aa77f6cSMauro Carvalho Chehab } 12180aa77f6cSMauro Carvalho Chehab 12190aa77f6cSMauro Carvalho Chehab static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 12200aa77f6cSMauro Carvalho Chehab { 12210aa77f6cSMauro Carvalho Chehab *i = 0; 12220aa77f6cSMauro Carvalho Chehab return 0; 12230aa77f6cSMauro Carvalho Chehab } 12240aa77f6cSMauro Carvalho Chehab static int vidioc_s_input(struct file *file, void *priv, unsigned int i) 12250aa77f6cSMauro Carvalho Chehab { 12260aa77f6cSMauro Carvalho Chehab if (i > 0) 12270aa77f6cSMauro Carvalho Chehab return -EINVAL; 12280aa77f6cSMauro Carvalho Chehab return 0; 12290aa77f6cSMauro Carvalho Chehab } 12300aa77f6cSMauro Carvalho Chehab 1231192f1e78SHans Verkuil static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) 12320aa77f6cSMauro Carvalho Chehab { 12335e950fafSDean Anderson struct s2255_vc *vc = 12345e950fafSDean Anderson container_of(ctrl->handler, struct s2255_vc, hdl); 12350aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 12365e950fafSDean Anderson mode = vc->mode; 12370aa77f6cSMauro Carvalho Chehab /* update the mode to the corresponding value */ 12380aa77f6cSMauro Carvalho Chehab switch (ctrl->id) { 12390aa77f6cSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 1240192f1e78SHans Verkuil mode.bright = ctrl->val; 12410aa77f6cSMauro Carvalho Chehab break; 12420aa77f6cSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 1243192f1e78SHans Verkuil mode.contrast = ctrl->val; 12440aa77f6cSMauro Carvalho Chehab break; 12450aa77f6cSMauro Carvalho Chehab case V4L2_CID_HUE: 1246192f1e78SHans Verkuil mode.hue = ctrl->val; 12470aa77f6cSMauro Carvalho Chehab break; 12480aa77f6cSMauro Carvalho Chehab case V4L2_CID_SATURATION: 1249192f1e78SHans Verkuil mode.saturation = ctrl->val; 12500aa77f6cSMauro Carvalho Chehab break; 1251192f1e78SHans Verkuil case V4L2_CID_S2255_COLORFILTER: 12520aa77f6cSMauro Carvalho Chehab mode.color &= ~MASK_INPUT_TYPE; 1253192f1e78SHans Verkuil mode.color |= !ctrl->val << 16; 12540aa77f6cSMauro Carvalho Chehab break; 12557041dec7SHans Verkuil case V4L2_CID_JPEG_COMPRESSION_QUALITY: 12565e950fafSDean Anderson vc->jpegqual = ctrl->val; 12577041dec7SHans Verkuil return 0; 12580aa77f6cSMauro Carvalho Chehab default: 12590aa77f6cSMauro Carvalho Chehab return -EINVAL; 12600aa77f6cSMauro Carvalho Chehab } 12610aa77f6cSMauro Carvalho Chehab mode.restart = 0; 12620aa77f6cSMauro Carvalho Chehab /* set mode here. Note: stream does not need restarted. 12630aa77f6cSMauro Carvalho Chehab some V4L programs restart stream unnecessarily 12640aa77f6cSMauro Carvalho Chehab after a s_crtl. 12650aa77f6cSMauro Carvalho Chehab */ 12665e950fafSDean Anderson s2255_set_mode(vc, &mode); 12670aa77f6cSMauro Carvalho Chehab return 0; 12680aa77f6cSMauro Carvalho Chehab } 12690aa77f6cSMauro Carvalho Chehab 12700aa77f6cSMauro Carvalho Chehab static int vidioc_g_jpegcomp(struct file *file, void *priv, 12710aa77f6cSMauro Carvalho Chehab struct v4l2_jpegcompression *jc) 12720aa77f6cSMauro Carvalho Chehab { 1273340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 12747041dec7SHans Verkuil 12757041dec7SHans Verkuil memset(jc, 0, sizeof(*jc)); 12765e950fafSDean Anderson jc->quality = vc->jpegqual; 127792cde477SDean Anderson dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality); 12780aa77f6cSMauro Carvalho Chehab return 0; 12790aa77f6cSMauro Carvalho Chehab } 12800aa77f6cSMauro Carvalho Chehab 12810aa77f6cSMauro Carvalho Chehab static int vidioc_s_jpegcomp(struct file *file, void *priv, 1282d88aab53SHans Verkuil const struct v4l2_jpegcompression *jc) 12830aa77f6cSMauro Carvalho Chehab { 1284340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 1285340a30c5Ssensoray-dev 12860aa77f6cSMauro Carvalho Chehab if (jc->quality < 0 || jc->quality > 100) 12870aa77f6cSMauro Carvalho Chehab return -EINVAL; 12885e950fafSDean Anderson v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality); 128992cde477SDean Anderson dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality); 12900aa77f6cSMauro Carvalho Chehab return 0; 12910aa77f6cSMauro Carvalho Chehab } 12920aa77f6cSMauro Carvalho Chehab 12930aa77f6cSMauro Carvalho Chehab static int vidioc_g_parm(struct file *file, void *priv, 12940aa77f6cSMauro Carvalho Chehab struct v4l2_streamparm *sp) 12950aa77f6cSMauro Carvalho Chehab { 12960aa77f6cSMauro Carvalho Chehab __u32 def_num, def_dem; 1297340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 1298340a30c5Ssensoray-dev 12990aa77f6cSMauro Carvalho Chehab if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 13000aa77f6cSMauro Carvalho Chehab return -EINVAL; 13010aa77f6cSMauro Carvalho Chehab sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 13025e950fafSDean Anderson sp->parm.capture.capturemode = vc->cap_parm.capturemode; 1303340a30c5Ssensoray-dev sp->parm.capture.readbuffers = S2255_MIN_BUFS; 13045e950fafSDean Anderson def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000; 13055e950fafSDean Anderson def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000; 13060aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator = def_dem; 13075e950fafSDean Anderson switch (vc->mode.fdec) { 13080aa77f6cSMauro Carvalho Chehab default: 13090aa77f6cSMauro Carvalho Chehab case FDEC_1: 13100aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num; 13110aa77f6cSMauro Carvalho Chehab break; 13120aa77f6cSMauro Carvalho Chehab case FDEC_2: 13130aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 2; 13140aa77f6cSMauro Carvalho Chehab break; 13150aa77f6cSMauro Carvalho Chehab case FDEC_3: 13160aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 3; 13170aa77f6cSMauro Carvalho Chehab break; 13180aa77f6cSMauro Carvalho Chehab case FDEC_5: 13190aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 5; 13200aa77f6cSMauro Carvalho Chehab break; 13210aa77f6cSMauro Carvalho Chehab } 132292cde477SDean Anderson dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n", 1323f5402007Ssensoray-dev __func__, 13240aa77f6cSMauro Carvalho Chehab sp->parm.capture.capturemode, 13250aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator, 13260aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator); 13270aa77f6cSMauro Carvalho Chehab return 0; 13280aa77f6cSMauro Carvalho Chehab } 13290aa77f6cSMauro Carvalho Chehab 13300aa77f6cSMauro Carvalho Chehab static int vidioc_s_parm(struct file *file, void *priv, 13310aa77f6cSMauro Carvalho Chehab struct v4l2_streamparm *sp) 13320aa77f6cSMauro Carvalho Chehab { 1333340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 13340aa77f6cSMauro Carvalho Chehab struct s2255_mode mode; 13350aa77f6cSMauro Carvalho Chehab int fdec = FDEC_1; 13360aa77f6cSMauro Carvalho Chehab __u32 def_num, def_dem; 13370aa77f6cSMauro Carvalho Chehab if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 13380aa77f6cSMauro Carvalho Chehab return -EINVAL; 13395e950fafSDean Anderson mode = vc->mode; 13400aa77f6cSMauro Carvalho Chehab /* high quality capture mode requires a stream restart */ 1341340a30c5Ssensoray-dev if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode) 1342340a30c5Ssensoray-dev && vb2_is_streaming(&vc->vb_vidq)) 13430aa77f6cSMauro Carvalho Chehab return -EBUSY; 13440aa77f6cSMauro Carvalho Chehab def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; 13450aa77f6cSMauro Carvalho Chehab def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; 13460aa77f6cSMauro Carvalho Chehab if (def_dem != sp->parm.capture.timeperframe.denominator) 13470aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num; 13480aa77f6cSMauro Carvalho Chehab else if (sp->parm.capture.timeperframe.numerator <= def_num) 13490aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num; 13500aa77f6cSMauro Carvalho Chehab else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { 13510aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 2; 13520aa77f6cSMauro Carvalho Chehab fdec = FDEC_2; 13530aa77f6cSMauro Carvalho Chehab } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { 13540aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 3; 13550aa77f6cSMauro Carvalho Chehab fdec = FDEC_3; 13560aa77f6cSMauro Carvalho Chehab } else { 13570aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator = def_num * 5; 13580aa77f6cSMauro Carvalho Chehab fdec = FDEC_5; 13590aa77f6cSMauro Carvalho Chehab } 13600aa77f6cSMauro Carvalho Chehab mode.fdec = fdec; 13610aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator = def_dem; 1362340a30c5Ssensoray-dev sp->parm.capture.readbuffers = S2255_MIN_BUFS; 13635e950fafSDean Anderson s2255_set_mode(vc, &mode); 136492cde477SDean Anderson dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", 13650aa77f6cSMauro Carvalho Chehab __func__, 13660aa77f6cSMauro Carvalho Chehab sp->parm.capture.capturemode, 13670aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.numerator, 13680aa77f6cSMauro Carvalho Chehab sp->parm.capture.timeperframe.denominator, fdec); 13690aa77f6cSMauro Carvalho Chehab return 0; 13700aa77f6cSMauro Carvalho Chehab } 13710aa77f6cSMauro Carvalho Chehab 137205e5d44bSHans Verkuil #define NUM_SIZE_ENUMS 3 137305e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete ntsc_sizes[] = { 137405e5d44bSHans Verkuil { 640, 480 }, 137505e5d44bSHans Verkuil { 640, 240 }, 137605e5d44bSHans Verkuil { 320, 240 }, 137705e5d44bSHans Verkuil }; 137805e5d44bSHans Verkuil static const struct v4l2_frmsize_discrete pal_sizes[] = { 137905e5d44bSHans Verkuil { 704, 576 }, 138005e5d44bSHans Verkuil { 704, 288 }, 138105e5d44bSHans Verkuil { 352, 288 }, 138205e5d44bSHans Verkuil }; 138305e5d44bSHans Verkuil 138405e5d44bSHans Verkuil static int vidioc_enum_framesizes(struct file *file, void *priv, 138505e5d44bSHans Verkuil struct v4l2_frmsizeenum *fe) 138605e5d44bSHans Verkuil { 1387340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 13885e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 138905e5d44bSHans Verkuil const struct s2255_fmt *fmt; 139005e5d44bSHans Verkuil 139105e5d44bSHans Verkuil if (fe->index >= NUM_SIZE_ENUMS) 139205e5d44bSHans Verkuil return -EINVAL; 139305e5d44bSHans Verkuil 139405e5d44bSHans Verkuil fmt = format_by_fourcc(fe->pixel_format); 139505e5d44bSHans Verkuil if (fmt == NULL) 139605e5d44bSHans Verkuil return -EINVAL; 139705e5d44bSHans Verkuil fe->type = V4L2_FRMSIZE_TYPE_DISCRETE; 139805e5d44bSHans Verkuil fe->discrete = is_ntsc ? ntsc_sizes[fe->index] : pal_sizes[fe->index]; 139905e5d44bSHans Verkuil return 0; 140005e5d44bSHans Verkuil } 140105e5d44bSHans Verkuil 14020aa77f6cSMauro Carvalho Chehab static int vidioc_enum_frameintervals(struct file *file, void *priv, 14030aa77f6cSMauro Carvalho Chehab struct v4l2_frmivalenum *fe) 14040aa77f6cSMauro Carvalho Chehab { 1405340a30c5Ssensoray-dev struct s2255_vc *vc = video_drvdata(file); 140605e5d44bSHans Verkuil const struct s2255_fmt *fmt; 140705e5d44bSHans Verkuil const struct v4l2_frmsize_discrete *sizes; 14085e950fafSDean Anderson int is_ntsc = vc->std & V4L2_STD_525_60; 14090aa77f6cSMauro Carvalho Chehab #define NUM_FRAME_ENUMS 4 14100aa77f6cSMauro Carvalho Chehab int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; 141105e5d44bSHans Verkuil int i; 141205e5d44bSHans Verkuil 1413199ab8feSMauro Carvalho Chehab if (fe->index >= NUM_FRAME_ENUMS) 14140aa77f6cSMauro Carvalho Chehab return -EINVAL; 141505e5d44bSHans Verkuil 141605e5d44bSHans Verkuil fmt = format_by_fourcc(fe->pixel_format); 141705e5d44bSHans Verkuil if (fmt == NULL) 14180aa77f6cSMauro Carvalho Chehab return -EINVAL; 141905e5d44bSHans Verkuil 142005e5d44bSHans Verkuil sizes = is_ntsc ? ntsc_sizes : pal_sizes; 142105e5d44bSHans Verkuil for (i = 0; i < NUM_SIZE_ENUMS; i++, sizes++) 142205e5d44bSHans Verkuil if (fe->width == sizes->width && 142305e5d44bSHans Verkuil fe->height == sizes->height) 14240aa77f6cSMauro Carvalho Chehab break; 142505e5d44bSHans Verkuil if (i == NUM_SIZE_ENUMS) 14260aa77f6cSMauro Carvalho Chehab return -EINVAL; 142705e5d44bSHans Verkuil 14280aa77f6cSMauro Carvalho Chehab fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; 14290aa77f6cSMauro Carvalho Chehab fe->discrete.denominator = is_ntsc ? 30000 : 25000; 14300aa77f6cSMauro Carvalho Chehab fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; 143192cde477SDean Anderson dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__, 1432f5402007Ssensoray-dev fe->discrete.numerator, 14330aa77f6cSMauro Carvalho Chehab fe->discrete.denominator); 14340aa77f6cSMauro Carvalho Chehab return 0; 14350aa77f6cSMauro Carvalho Chehab } 14360aa77f6cSMauro Carvalho Chehab 1437340a30c5Ssensoray-dev static int s2255_open(struct file *file) 14380aa77f6cSMauro Carvalho Chehab { 14395e950fafSDean Anderson struct s2255_vc *vc = video_drvdata(file); 1440340a30c5Ssensoray-dev struct s2255_dev *dev = vc->dev; 14410aa77f6cSMauro Carvalho Chehab int state; 1442340a30c5Ssensoray-dev int rc = 0; 1443340a30c5Ssensoray-dev 1444340a30c5Ssensoray-dev rc = v4l2_fh_open(file); 1445340a30c5Ssensoray-dev if (rc != 0) 1446340a30c5Ssensoray-dev return rc; 1447340a30c5Ssensoray-dev 1448340a30c5Ssensoray-dev dprintk(dev, 1, "s2255: %s\n", __func__); 14490aa77f6cSMauro Carvalho Chehab state = atomic_read(&dev->fw_data->fw_state); 14500aa77f6cSMauro Carvalho Chehab switch (state) { 14510aa77f6cSMauro Carvalho Chehab case S2255_FW_DISCONNECTING: 14520aa77f6cSMauro Carvalho Chehab return -ENODEV; 14530aa77f6cSMauro Carvalho Chehab case S2255_FW_FAILED: 14540aa77f6cSMauro Carvalho Chehab s2255_dev_err(&dev->udev->dev, 14550aa77f6cSMauro Carvalho Chehab "firmware load failed. retrying.\n"); 14560aa77f6cSMauro Carvalho Chehab s2255_fwload_start(dev, 1); 14570aa77f6cSMauro Carvalho Chehab wait_event_timeout(dev->fw_data->wait_fw, 14580aa77f6cSMauro Carvalho Chehab ((atomic_read(&dev->fw_data->fw_state) 14590aa77f6cSMauro Carvalho Chehab == S2255_FW_SUCCESS) || 14600aa77f6cSMauro Carvalho Chehab (atomic_read(&dev->fw_data->fw_state) 14610aa77f6cSMauro Carvalho Chehab == S2255_FW_DISCONNECTING)), 14620aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_LOAD_TIMEOUT)); 14630aa77f6cSMauro Carvalho Chehab /* state may have changed, re-read */ 14640aa77f6cSMauro Carvalho Chehab state = atomic_read(&dev->fw_data->fw_state); 14650aa77f6cSMauro Carvalho Chehab break; 14660aa77f6cSMauro Carvalho Chehab case S2255_FW_NOTLOADED: 14670aa77f6cSMauro Carvalho Chehab case S2255_FW_LOADED_DSPWAIT: 14680aa77f6cSMauro Carvalho Chehab /* give S2255_LOAD_TIMEOUT time for firmware to load in case 14690aa77f6cSMauro Carvalho Chehab driver loaded and then device immediately opened */ 1470f5402007Ssensoray-dev pr_info("%s waiting for firmware load\n", __func__); 14710aa77f6cSMauro Carvalho Chehab wait_event_timeout(dev->fw_data->wait_fw, 14720aa77f6cSMauro Carvalho Chehab ((atomic_read(&dev->fw_data->fw_state) 14730aa77f6cSMauro Carvalho Chehab == S2255_FW_SUCCESS) || 14740aa77f6cSMauro Carvalho Chehab (atomic_read(&dev->fw_data->fw_state) 14750aa77f6cSMauro Carvalho Chehab == S2255_FW_DISCONNECTING)), 14760aa77f6cSMauro Carvalho Chehab msecs_to_jiffies(S2255_LOAD_TIMEOUT)); 14770aa77f6cSMauro Carvalho Chehab /* state may have changed, re-read */ 14780aa77f6cSMauro Carvalho Chehab state = atomic_read(&dev->fw_data->fw_state); 14790aa77f6cSMauro Carvalho Chehab break; 14800aa77f6cSMauro Carvalho Chehab case S2255_FW_SUCCESS: 14810aa77f6cSMauro Carvalho Chehab default: 14820aa77f6cSMauro Carvalho Chehab break; 14830aa77f6cSMauro Carvalho Chehab } 14840aa77f6cSMauro Carvalho Chehab /* state may have changed in above switch statement */ 14850aa77f6cSMauro Carvalho Chehab switch (state) { 14860aa77f6cSMauro Carvalho Chehab case S2255_FW_SUCCESS: 14870aa77f6cSMauro Carvalho Chehab break; 14880aa77f6cSMauro Carvalho Chehab case S2255_FW_FAILED: 1489f5402007Ssensoray-dev pr_info("2255 firmware load failed.\n"); 14900aa77f6cSMauro Carvalho Chehab return -ENODEV; 14910aa77f6cSMauro Carvalho Chehab case S2255_FW_DISCONNECTING: 1492f5402007Ssensoray-dev pr_info("%s: disconnecting\n", __func__); 14930aa77f6cSMauro Carvalho Chehab return -ENODEV; 14940aa77f6cSMauro Carvalho Chehab case S2255_FW_LOADED_DSPWAIT: 14950aa77f6cSMauro Carvalho Chehab case S2255_FW_NOTLOADED: 1496f5402007Ssensoray-dev pr_info("%s: firmware not loaded, please retry\n", 14970aa77f6cSMauro Carvalho Chehab __func__); 14980aa77f6cSMauro Carvalho Chehab /* 14990aa77f6cSMauro Carvalho Chehab * Timeout on firmware load means device unusable. 15000aa77f6cSMauro Carvalho Chehab * Set firmware failure state. 15010aa77f6cSMauro Carvalho Chehab * On next s2255_open the firmware will be reloaded. 15020aa77f6cSMauro Carvalho Chehab */ 15030aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, 15040aa77f6cSMauro Carvalho Chehab S2255_FW_FAILED); 15050aa77f6cSMauro Carvalho Chehab return -EAGAIN; 15060aa77f6cSMauro Carvalho Chehab default: 1507f5402007Ssensoray-dev pr_info("%s: unknown state\n", __func__); 15080aa77f6cSMauro Carvalho Chehab return -EFAULT; 15090aa77f6cSMauro Carvalho Chehab } 15105e950fafSDean Anderson if (!vc->configured) { 15110aa77f6cSMauro Carvalho Chehab /* configure channel to default state */ 15125e950fafSDean Anderson vc->fmt = &formats[0]; 15135e950fafSDean Anderson s2255_set_mode(vc, &vc->mode); 15145e950fafSDean Anderson vc->configured = 1; 15150aa77f6cSMauro Carvalho Chehab } 15160aa77f6cSMauro Carvalho Chehab return 0; 15170aa77f6cSMauro Carvalho Chehab } 15180aa77f6cSMauro Carvalho Chehab 15190aa77f6cSMauro Carvalho Chehab static void s2255_destroy(struct s2255_dev *dev) 15200aa77f6cSMauro Carvalho Chehab { 1521f5402007Ssensoray-dev dprintk(dev, 1, "%s", __func__); 15220aa77f6cSMauro Carvalho Chehab /* board shutdown stops the read pipe if it is running */ 15230aa77f6cSMauro Carvalho Chehab s2255_board_shutdown(dev); 15240aa77f6cSMauro Carvalho Chehab /* make sure firmware still not trying to load */ 15250aa77f6cSMauro Carvalho Chehab del_timer(&dev->timer); /* only started in .probe and .open */ 15260aa77f6cSMauro Carvalho Chehab if (dev->fw_data->fw_urb) { 15270aa77f6cSMauro Carvalho Chehab usb_kill_urb(dev->fw_data->fw_urb); 15280aa77f6cSMauro Carvalho Chehab usb_free_urb(dev->fw_data->fw_urb); 15290aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_urb = NULL; 15300aa77f6cSMauro Carvalho Chehab } 15310aa77f6cSMauro Carvalho Chehab release_firmware(dev->fw_data->fw); 15320aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data->pfw_data); 15330aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data); 15340aa77f6cSMauro Carvalho Chehab /* reset the DSP so firmware can be reloaded next time */ 15350aa77f6cSMauro Carvalho Chehab s2255_reset_dsppower(dev); 15360aa77f6cSMauro Carvalho Chehab mutex_destroy(&dev->lock); 15370aa77f6cSMauro Carvalho Chehab usb_put_dev(dev->udev); 15380aa77f6cSMauro Carvalho Chehab v4l2_device_unregister(&dev->v4l2_dev); 153947d8c881SDean Anderson kfree(dev->cmdbuf); 15400aa77f6cSMauro Carvalho Chehab kfree(dev); 15410aa77f6cSMauro Carvalho Chehab } 15420aa77f6cSMauro Carvalho Chehab 15430aa77f6cSMauro Carvalho Chehab static const struct v4l2_file_operations s2255_fops_v4l = { 15440aa77f6cSMauro Carvalho Chehab .owner = THIS_MODULE, 15450aa77f6cSMauro Carvalho Chehab .open = s2255_open, 1546340a30c5Ssensoray-dev .release = vb2_fop_release, 1547340a30c5Ssensoray-dev .poll = vb2_fop_poll, 15480aa77f6cSMauro Carvalho Chehab .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ 1549340a30c5Ssensoray-dev .mmap = vb2_fop_mmap, 1550340a30c5Ssensoray-dev .read = vb2_fop_read, 15510aa77f6cSMauro Carvalho Chehab }; 15520aa77f6cSMauro Carvalho Chehab 15530aa77f6cSMauro Carvalho Chehab static const struct v4l2_ioctl_ops s2255_ioctl_ops = { 15540aa77f6cSMauro Carvalho Chehab .vidioc_querycap = vidioc_querycap, 15550aa77f6cSMauro Carvalho Chehab .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 15560aa77f6cSMauro Carvalho Chehab .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 15570aa77f6cSMauro Carvalho Chehab .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 15580aa77f6cSMauro Carvalho Chehab .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 1559340a30c5Ssensoray-dev .vidioc_reqbufs = vb2_ioctl_reqbufs, 1560340a30c5Ssensoray-dev .vidioc_querybuf = vb2_ioctl_querybuf, 1561340a30c5Ssensoray-dev .vidioc_qbuf = vb2_ioctl_qbuf, 1562340a30c5Ssensoray-dev .vidioc_dqbuf = vb2_ioctl_dqbuf, 15630aa77f6cSMauro Carvalho Chehab .vidioc_s_std = vidioc_s_std, 1564469af77aSHans Verkuil .vidioc_g_std = vidioc_g_std, 15650aa77f6cSMauro Carvalho Chehab .vidioc_enum_input = vidioc_enum_input, 15660aa77f6cSMauro Carvalho Chehab .vidioc_g_input = vidioc_g_input, 15670aa77f6cSMauro Carvalho Chehab .vidioc_s_input = vidioc_s_input, 1568340a30c5Ssensoray-dev .vidioc_streamon = vb2_ioctl_streamon, 1569340a30c5Ssensoray-dev .vidioc_streamoff = vb2_ioctl_streamoff, 15700aa77f6cSMauro Carvalho Chehab .vidioc_s_jpegcomp = vidioc_s_jpegcomp, 15710aa77f6cSMauro Carvalho Chehab .vidioc_g_jpegcomp = vidioc_g_jpegcomp, 15720aa77f6cSMauro Carvalho Chehab .vidioc_s_parm = vidioc_s_parm, 15730aa77f6cSMauro Carvalho Chehab .vidioc_g_parm = vidioc_g_parm, 157405e5d44bSHans Verkuil .vidioc_enum_framesizes = vidioc_enum_framesizes, 15750aa77f6cSMauro Carvalho Chehab .vidioc_enum_frameintervals = vidioc_enum_frameintervals, 157644d06d82SHans Verkuil .vidioc_log_status = v4l2_ctrl_log_status, 157744d06d82SHans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 157844d06d82SHans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 15790aa77f6cSMauro Carvalho Chehab }; 15800aa77f6cSMauro Carvalho Chehab 15810aa77f6cSMauro Carvalho Chehab static void s2255_video_device_release(struct video_device *vdev) 15820aa77f6cSMauro Carvalho Chehab { 15830aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); 15845e950fafSDean Anderson struct s2255_vc *vc = 15855e950fafSDean Anderson container_of(vdev, struct s2255_vc, vdev); 1586192f1e78SHans Verkuil 1587f5402007Ssensoray-dev dprintk(dev, 4, "%s, chnls: %d\n", __func__, 15880aa77f6cSMauro Carvalho Chehab atomic_read(&dev->num_channels)); 1589192f1e78SHans Verkuil 15905e950fafSDean Anderson v4l2_ctrl_handler_free(&vc->hdl); 1591f5402007Ssensoray-dev 15920aa77f6cSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->num_channels)) 15930aa77f6cSMauro Carvalho Chehab s2255_destroy(dev); 15940aa77f6cSMauro Carvalho Chehab return; 15950aa77f6cSMauro Carvalho Chehab } 15960aa77f6cSMauro Carvalho Chehab 15970aa77f6cSMauro Carvalho Chehab static struct video_device template = { 15980aa77f6cSMauro Carvalho Chehab .name = "s2255v", 15990aa77f6cSMauro Carvalho Chehab .fops = &s2255_fops_v4l, 16000aa77f6cSMauro Carvalho Chehab .ioctl_ops = &s2255_ioctl_ops, 16010aa77f6cSMauro Carvalho Chehab .release = s2255_video_device_release, 16020aa77f6cSMauro Carvalho Chehab .tvnorms = S2255_NORMS, 16030aa77f6cSMauro Carvalho Chehab }; 16040aa77f6cSMauro Carvalho Chehab 1605192f1e78SHans Verkuil static const struct v4l2_ctrl_ops s2255_ctrl_ops = { 1606192f1e78SHans Verkuil .s_ctrl = s2255_s_ctrl, 1607192f1e78SHans Verkuil }; 1608192f1e78SHans Verkuil 1609192f1e78SHans Verkuil static const struct v4l2_ctrl_config color_filter_ctrl = { 1610192f1e78SHans Verkuil .ops = &s2255_ctrl_ops, 1611192f1e78SHans Verkuil .name = "Color Filter", 1612192f1e78SHans Verkuil .id = V4L2_CID_S2255_COLORFILTER, 1613192f1e78SHans Verkuil .type = V4L2_CTRL_TYPE_BOOLEAN, 1614192f1e78SHans Verkuil .max = 1, 1615192f1e78SHans Verkuil .step = 1, 1616192f1e78SHans Verkuil .def = 1, 1617192f1e78SHans Verkuil }; 1618192f1e78SHans Verkuil 16190aa77f6cSMauro Carvalho Chehab static int s2255_probe_v4l(struct s2255_dev *dev) 16200aa77f6cSMauro Carvalho Chehab { 16210aa77f6cSMauro Carvalho Chehab int ret; 16220aa77f6cSMauro Carvalho Chehab int i; 16230aa77f6cSMauro Carvalho Chehab int cur_nr = video_nr; 16245e950fafSDean Anderson struct s2255_vc *vc; 1625340a30c5Ssensoray-dev struct vb2_queue *q; 1626340a30c5Ssensoray-dev 16270aa77f6cSMauro Carvalho Chehab ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); 16280aa77f6cSMauro Carvalho Chehab if (ret) 16290aa77f6cSMauro Carvalho Chehab return ret; 16300aa77f6cSMauro Carvalho Chehab /* initialize all video 4 linux */ 16310aa77f6cSMauro Carvalho Chehab /* register 4 video devices */ 16320aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 16335e950fafSDean Anderson vc = &dev->vc[i]; 16345e950fafSDean Anderson INIT_LIST_HEAD(&vc->buf_list); 1635192f1e78SHans Verkuil 16365e950fafSDean Anderson v4l2_ctrl_handler_init(&vc->hdl, 6); 16375e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1638192f1e78SHans Verkuil V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT); 16395e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1640192f1e78SHans Verkuil V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST); 16415e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1642192f1e78SHans Verkuil V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION); 16435e950fafSDean Anderson v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, 1644192f1e78SHans Verkuil V4L2_CID_HUE, 0, 255, 1, DEF_HUE); 16455e950fafSDean Anderson vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl, 16467041dec7SHans Verkuil &s2255_ctrl_ops, 16477041dec7SHans Verkuil V4L2_CID_JPEG_COMPRESSION_QUALITY, 16487041dec7SHans Verkuil 0, 100, 1, S2255_DEF_JPEG_QUAL); 1649192f1e78SHans Verkuil if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER && 16505e950fafSDean Anderson (dev->pid != 0x2257 || vc->idx <= 1)) 16515e950fafSDean Anderson v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl, 1652f5402007Ssensoray-dev NULL); 16535e950fafSDean Anderson if (vc->hdl.error) { 16545e950fafSDean Anderson ret = vc->hdl.error; 16555e950fafSDean Anderson v4l2_ctrl_handler_free(&vc->hdl); 1656192f1e78SHans Verkuil dev_err(&dev->udev->dev, "couldn't register control\n"); 1657192f1e78SHans Verkuil break; 1658192f1e78SHans Verkuil } 1659340a30c5Ssensoray-dev q = &vc->vb_vidq; 1660340a30c5Ssensoray-dev q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1661340a30c5Ssensoray-dev q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR; 1662340a30c5Ssensoray-dev q->drv_priv = vc; 1663340a30c5Ssensoray-dev q->lock = &vc->vb_lock; 1664340a30c5Ssensoray-dev q->buf_struct_size = sizeof(struct s2255_buffer); 1665340a30c5Ssensoray-dev q->mem_ops = &vb2_vmalloc_memops; 1666340a30c5Ssensoray-dev q->ops = &s2255_video_qops; 1667*ade48681SSakari Ailus q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 1668340a30c5Ssensoray-dev ret = vb2_queue_init(q); 1669340a30c5Ssensoray-dev if (ret != 0) { 1670340a30c5Ssensoray-dev dev_err(&dev->udev->dev, 1671340a30c5Ssensoray-dev "%s vb2_queue_init 0x%x\n", __func__, ret); 1672340a30c5Ssensoray-dev break; 1673340a30c5Ssensoray-dev } 1674340a30c5Ssensoray-dev /* register video devices */ 16755e950fafSDean Anderson vc->vdev = template; 1676340a30c5Ssensoray-dev vc->vdev.queue = q; 16775e950fafSDean Anderson vc->vdev.ctrl_handler = &vc->hdl; 16785e950fafSDean Anderson vc->vdev.lock = &dev->lock; 16795e950fafSDean Anderson vc->vdev.v4l2_dev = &dev->v4l2_dev; 16805e950fafSDean Anderson set_bit(V4L2_FL_USE_FH_PRIO, &vc->vdev.flags); 16815e950fafSDean Anderson video_set_drvdata(&vc->vdev, vc); 16820aa77f6cSMauro Carvalho Chehab if (video_nr == -1) 16835e950fafSDean Anderson ret = video_register_device(&vc->vdev, 16840aa77f6cSMauro Carvalho Chehab VFL_TYPE_GRABBER, 16850aa77f6cSMauro Carvalho Chehab video_nr); 16860aa77f6cSMauro Carvalho Chehab else 16875e950fafSDean Anderson ret = video_register_device(&vc->vdev, 16880aa77f6cSMauro Carvalho Chehab VFL_TYPE_GRABBER, 16890aa77f6cSMauro Carvalho Chehab cur_nr + i); 16900aa77f6cSMauro Carvalho Chehab 16910aa77f6cSMauro Carvalho Chehab if (ret) { 16920aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, 16930aa77f6cSMauro Carvalho Chehab "failed to register video device!\n"); 16940aa77f6cSMauro Carvalho Chehab break; 16950aa77f6cSMauro Carvalho Chehab } 16960aa77f6cSMauro Carvalho Chehab atomic_inc(&dev->num_channels); 16970aa77f6cSMauro Carvalho Chehab v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", 16985e950fafSDean Anderson video_device_node_name(&vc->vdev)); 16990aa77f6cSMauro Carvalho Chehab 17000aa77f6cSMauro Carvalho Chehab } 1701f5402007Ssensoray-dev pr_info("Sensoray 2255 V4L driver Revision: %s\n", 17020aa77f6cSMauro Carvalho Chehab S2255_VERSION); 17030aa77f6cSMauro Carvalho Chehab /* if no channels registered, return error and probe will fail*/ 17040aa77f6cSMauro Carvalho Chehab if (atomic_read(&dev->num_channels) == 0) { 17050aa77f6cSMauro Carvalho Chehab v4l2_device_unregister(&dev->v4l2_dev); 17060aa77f6cSMauro Carvalho Chehab return ret; 17070aa77f6cSMauro Carvalho Chehab } 17080aa77f6cSMauro Carvalho Chehab if (atomic_read(&dev->num_channels) != MAX_CHANNELS) 1709f5402007Ssensoray-dev pr_warn("s2255: Not all channels available.\n"); 17100aa77f6cSMauro Carvalho Chehab return 0; 17110aa77f6cSMauro Carvalho Chehab } 17120aa77f6cSMauro Carvalho Chehab 17130aa77f6cSMauro Carvalho Chehab /* this function moves the usb stream read pipe data 17140aa77f6cSMauro Carvalho Chehab * into the system buffers. 17150aa77f6cSMauro Carvalho Chehab * returns 0 on success, EAGAIN if more data to process( call this 17160aa77f6cSMauro Carvalho Chehab * function again). 17170aa77f6cSMauro Carvalho Chehab * 17180aa77f6cSMauro Carvalho Chehab * Received frame structure: 17190aa77f6cSMauro Carvalho Chehab * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) 17200aa77f6cSMauro Carvalho Chehab * bytes 4-7: channel: 0-3 17210aa77f6cSMauro Carvalho Chehab * bytes 8-11: payload size: size of the frame 17220aa77f6cSMauro Carvalho Chehab * bytes 12-payloadsize+12: frame data 17230aa77f6cSMauro Carvalho Chehab */ 17240aa77f6cSMauro Carvalho Chehab static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) 17250aa77f6cSMauro Carvalho Chehab { 17260aa77f6cSMauro Carvalho Chehab char *pdest; 17270aa77f6cSMauro Carvalho Chehab u32 offset = 0; 17280aa77f6cSMauro Carvalho Chehab int bframe = 0; 17290aa77f6cSMauro Carvalho Chehab char *psrc; 17300aa77f6cSMauro Carvalho Chehab unsigned long copy_size; 17310aa77f6cSMauro Carvalho Chehab unsigned long size; 17320aa77f6cSMauro Carvalho Chehab s32 idx = -1; 17330aa77f6cSMauro Carvalho Chehab struct s2255_framei *frm; 17340aa77f6cSMauro Carvalho Chehab unsigned char *pdata; 17355e950fafSDean Anderson struct s2255_vc *vc; 1736f5402007Ssensoray-dev dprintk(dev, 100, "buffer to user\n"); 17375e950fafSDean Anderson vc = &dev->vc[dev->cc]; 17385e950fafSDean Anderson idx = vc->cur_frame; 17395e950fafSDean Anderson frm = &vc->buffer.frame[idx]; 17400aa77f6cSMauro Carvalho Chehab if (frm->ulState == S2255_READ_IDLE) { 17410aa77f6cSMauro Carvalho Chehab int jj; 17420aa77f6cSMauro Carvalho Chehab unsigned int cc; 17430aa77f6cSMauro Carvalho Chehab __le32 *pdword; /*data from dsp is little endian */ 17440aa77f6cSMauro Carvalho Chehab int payload; 17450aa77f6cSMauro Carvalho Chehab /* search for marker codes */ 17460aa77f6cSMauro Carvalho Chehab pdata = (unsigned char *)pipe_info->transfer_buffer; 17470aa77f6cSMauro Carvalho Chehab pdword = (__le32 *)pdata; 17480aa77f6cSMauro Carvalho Chehab for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { 17490aa77f6cSMauro Carvalho Chehab switch (*pdword) { 17500aa77f6cSMauro Carvalho Chehab case S2255_MARKER_FRAME: 1751f5402007Ssensoray-dev dprintk(dev, 4, "marker @ offset: %d [%x %x]\n", 1752f5402007Ssensoray-dev jj, pdata[0], pdata[1]); 17530aa77f6cSMauro Carvalho Chehab offset = jj + PREFIX_SIZE; 17540aa77f6cSMauro Carvalho Chehab bframe = 1; 17550aa77f6cSMauro Carvalho Chehab cc = le32_to_cpu(pdword[1]); 17560aa77f6cSMauro Carvalho Chehab if (cc >= MAX_CHANNELS) { 1757f5402007Ssensoray-dev dprintk(dev, 0, 17580aa77f6cSMauro Carvalho Chehab "bad channel\n"); 17590aa77f6cSMauro Carvalho Chehab return -EINVAL; 17600aa77f6cSMauro Carvalho Chehab } 17610aa77f6cSMauro Carvalho Chehab /* reverse it */ 17620aa77f6cSMauro Carvalho Chehab dev->cc = G_chnmap[cc]; 17635e950fafSDean Anderson vc = &dev->vc[dev->cc]; 17640aa77f6cSMauro Carvalho Chehab payload = le32_to_cpu(pdword[3]); 17655e950fafSDean Anderson if (payload > vc->req_image_size) { 17665e950fafSDean Anderson vc->bad_payload++; 17670aa77f6cSMauro Carvalho Chehab /* discard the bad frame */ 17680aa77f6cSMauro Carvalho Chehab return -EINVAL; 17690aa77f6cSMauro Carvalho Chehab } 17705e950fafSDean Anderson vc->pkt_size = payload; 17715e950fafSDean Anderson vc->jpg_size = le32_to_cpu(pdword[4]); 17720aa77f6cSMauro Carvalho Chehab break; 17730aa77f6cSMauro Carvalho Chehab case S2255_MARKER_RESPONSE: 17740aa77f6cSMauro Carvalho Chehab 17750aa77f6cSMauro Carvalho Chehab pdata += DEF_USB_BLOCK; 17760aa77f6cSMauro Carvalho Chehab jj += DEF_USB_BLOCK; 17770aa77f6cSMauro Carvalho Chehab if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) 17780aa77f6cSMauro Carvalho Chehab break; 17790aa77f6cSMauro Carvalho Chehab cc = G_chnmap[le32_to_cpu(pdword[1])]; 17800aa77f6cSMauro Carvalho Chehab if (cc >= MAX_CHANNELS) 17810aa77f6cSMauro Carvalho Chehab break; 17825e950fafSDean Anderson vc = &dev->vc[cc]; 17830aa77f6cSMauro Carvalho Chehab switch (pdword[2]) { 17840aa77f6cSMauro Carvalho Chehab case S2255_RESPONSE_SETMODE: 17850aa77f6cSMauro Carvalho Chehab /* check if channel valid */ 17860aa77f6cSMauro Carvalho Chehab /* set mode ready */ 17875e950fafSDean Anderson vc->setmode_ready = 1; 17885e950fafSDean Anderson wake_up(&vc->wait_setmode); 1789f5402007Ssensoray-dev dprintk(dev, 5, "setmode rdy %d\n", cc); 17900aa77f6cSMauro Carvalho Chehab break; 17910aa77f6cSMauro Carvalho Chehab case S2255_RESPONSE_FW: 17920aa77f6cSMauro Carvalho Chehab dev->chn_ready |= (1 << cc); 17930aa77f6cSMauro Carvalho Chehab if ((dev->chn_ready & 0x0f) != 0x0f) 17940aa77f6cSMauro Carvalho Chehab break; 17950aa77f6cSMauro Carvalho Chehab /* all channels ready */ 1796f5402007Ssensoray-dev pr_info("s2255: fw loaded\n"); 17970aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, 17980aa77f6cSMauro Carvalho Chehab S2255_FW_SUCCESS); 17990aa77f6cSMauro Carvalho Chehab wake_up(&dev->fw_data->wait_fw); 18000aa77f6cSMauro Carvalho Chehab break; 18010aa77f6cSMauro Carvalho Chehab case S2255_RESPONSE_STATUS: 18025e950fafSDean Anderson vc->vidstatus = le32_to_cpu(pdword[3]); 18035e950fafSDean Anderson vc->vidstatus_ready = 1; 18045e950fafSDean Anderson wake_up(&vc->wait_vidstatus); 1805f5402007Ssensoray-dev dprintk(dev, 5, "vstat %x chan %d\n", 18060aa77f6cSMauro Carvalho Chehab le32_to_cpu(pdword[3]), cc); 18070aa77f6cSMauro Carvalho Chehab break; 18080aa77f6cSMauro Carvalho Chehab default: 1809f5402007Ssensoray-dev pr_info("s2255 unknown resp\n"); 18100aa77f6cSMauro Carvalho Chehab } 18110aa77f6cSMauro Carvalho Chehab default: 18120aa77f6cSMauro Carvalho Chehab pdata++; 18130aa77f6cSMauro Carvalho Chehab break; 18140aa77f6cSMauro Carvalho Chehab } 18150aa77f6cSMauro Carvalho Chehab if (bframe) 18160aa77f6cSMauro Carvalho Chehab break; 18170aa77f6cSMauro Carvalho Chehab } /* for */ 18180aa77f6cSMauro Carvalho Chehab if (!bframe) 18190aa77f6cSMauro Carvalho Chehab return -EINVAL; 18200aa77f6cSMauro Carvalho Chehab } 18215e950fafSDean Anderson vc = &dev->vc[dev->cc]; 18225e950fafSDean Anderson idx = vc->cur_frame; 18235e950fafSDean Anderson frm = &vc->buffer.frame[idx]; 18240aa77f6cSMauro Carvalho Chehab /* search done. now find out if should be acquiring on this channel */ 1825340a30c5Ssensoray-dev if (!vb2_is_streaming(&vc->vb_vidq)) { 18260aa77f6cSMauro Carvalho Chehab /* we found a frame, but this channel is turned off */ 18270aa77f6cSMauro Carvalho Chehab frm->ulState = S2255_READ_IDLE; 18280aa77f6cSMauro Carvalho Chehab return -EINVAL; 18290aa77f6cSMauro Carvalho Chehab } 18300aa77f6cSMauro Carvalho Chehab 18310aa77f6cSMauro Carvalho Chehab if (frm->ulState == S2255_READ_IDLE) { 18320aa77f6cSMauro Carvalho Chehab frm->ulState = S2255_READ_FRAME; 18330aa77f6cSMauro Carvalho Chehab frm->cur_size = 0; 18340aa77f6cSMauro Carvalho Chehab } 18350aa77f6cSMauro Carvalho Chehab 18360aa77f6cSMauro Carvalho Chehab /* skip the marker 512 bytes (and offset if out of sync) */ 18370aa77f6cSMauro Carvalho Chehab psrc = (u8 *)pipe_info->transfer_buffer + offset; 18380aa77f6cSMauro Carvalho Chehab 18390aa77f6cSMauro Carvalho Chehab 18400aa77f6cSMauro Carvalho Chehab if (frm->lpvbits == NULL) { 1841f5402007Ssensoray-dev dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d", 18420aa77f6cSMauro Carvalho Chehab frm, dev, dev->cc, idx); 18430aa77f6cSMauro Carvalho Chehab return -ENOMEM; 18440aa77f6cSMauro Carvalho Chehab } 18450aa77f6cSMauro Carvalho Chehab 18460aa77f6cSMauro Carvalho Chehab pdest = frm->lpvbits + frm->cur_size; 18470aa77f6cSMauro Carvalho Chehab 18480aa77f6cSMauro Carvalho Chehab copy_size = (pipe_info->cur_transfer_size - offset); 18490aa77f6cSMauro Carvalho Chehab 18505e950fafSDean Anderson size = vc->pkt_size - PREFIX_SIZE; 18510aa77f6cSMauro Carvalho Chehab 18520aa77f6cSMauro Carvalho Chehab /* sanity check on pdest */ 18535e950fafSDean Anderson if ((copy_size + frm->cur_size) < vc->req_image_size) 18540aa77f6cSMauro Carvalho Chehab memcpy(pdest, psrc, copy_size); 18550aa77f6cSMauro Carvalho Chehab 18560aa77f6cSMauro Carvalho Chehab frm->cur_size += copy_size; 1857f5402007Ssensoray-dev dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size); 18580aa77f6cSMauro Carvalho Chehab 18590aa77f6cSMauro Carvalho Chehab if (frm->cur_size >= size) { 1860f5402007Ssensoray-dev dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n", 18610aa77f6cSMauro Carvalho Chehab dev->cc, idx); 18625e950fafSDean Anderson vc->last_frame = vc->cur_frame; 18635e950fafSDean Anderson vc->cur_frame++; 18640aa77f6cSMauro Carvalho Chehab /* end of system frame ring buffer, start at zero */ 18655e950fafSDean Anderson if ((vc->cur_frame == SYS_FRAMES) || 18665e950fafSDean Anderson (vc->cur_frame == vc->buffer.dwFrames)) 18675e950fafSDean Anderson vc->cur_frame = 0; 18680aa77f6cSMauro Carvalho Chehab /* frame ready */ 1869340a30c5Ssensoray-dev if (vb2_is_streaming(&vc->vb_vidq)) 18705e950fafSDean Anderson s2255_got_frame(vc, vc->jpg_size); 18715e950fafSDean Anderson vc->frame_count++; 18720aa77f6cSMauro Carvalho Chehab frm->ulState = S2255_READ_IDLE; 18730aa77f6cSMauro Carvalho Chehab frm->cur_size = 0; 18740aa77f6cSMauro Carvalho Chehab 18750aa77f6cSMauro Carvalho Chehab } 18760aa77f6cSMauro Carvalho Chehab /* done successfully */ 18770aa77f6cSMauro Carvalho Chehab return 0; 18780aa77f6cSMauro Carvalho Chehab } 18790aa77f6cSMauro Carvalho Chehab 18800aa77f6cSMauro Carvalho Chehab static void s2255_read_video_callback(struct s2255_dev *dev, 18810aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe_info) 18820aa77f6cSMauro Carvalho Chehab { 18830aa77f6cSMauro Carvalho Chehab int res; 1884f5402007Ssensoray-dev dprintk(dev, 50, "callback read video\n"); 18850aa77f6cSMauro Carvalho Chehab 18860aa77f6cSMauro Carvalho Chehab if (dev->cc >= MAX_CHANNELS) { 18870aa77f6cSMauro Carvalho Chehab dev->cc = 0; 18880aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "invalid channel\n"); 18890aa77f6cSMauro Carvalho Chehab return; 18900aa77f6cSMauro Carvalho Chehab } 18910aa77f6cSMauro Carvalho Chehab /* otherwise copy to the system buffers */ 18920aa77f6cSMauro Carvalho Chehab res = save_frame(dev, pipe_info); 18930aa77f6cSMauro Carvalho Chehab if (res != 0) 1894f5402007Ssensoray-dev dprintk(dev, 4, "s2255: read callback failed\n"); 18950aa77f6cSMauro Carvalho Chehab 1896f5402007Ssensoray-dev dprintk(dev, 50, "callback read video done\n"); 18970aa77f6cSMauro Carvalho Chehab return; 18980aa77f6cSMauro Carvalho Chehab } 18990aa77f6cSMauro Carvalho Chehab 19000aa77f6cSMauro Carvalho Chehab static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, 19010aa77f6cSMauro Carvalho Chehab u16 Index, u16 Value, void *TransferBuffer, 19020aa77f6cSMauro Carvalho Chehab s32 TransferBufferLength, int bOut) 19030aa77f6cSMauro Carvalho Chehab { 19040aa77f6cSMauro Carvalho Chehab int r; 19050aa77f6cSMauro Carvalho Chehab if (!bOut) { 19060aa77f6cSMauro Carvalho Chehab r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), 19070aa77f6cSMauro Carvalho Chehab Request, 19080aa77f6cSMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | 19090aa77f6cSMauro Carvalho Chehab USB_DIR_IN, 19100aa77f6cSMauro Carvalho Chehab Value, Index, TransferBuffer, 19110aa77f6cSMauro Carvalho Chehab TransferBufferLength, HZ * 5); 19120aa77f6cSMauro Carvalho Chehab } else { 19130aa77f6cSMauro Carvalho Chehab r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), 19140aa77f6cSMauro Carvalho Chehab Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 19150aa77f6cSMauro Carvalho Chehab Value, Index, TransferBuffer, 19160aa77f6cSMauro Carvalho Chehab TransferBufferLength, HZ * 5); 19170aa77f6cSMauro Carvalho Chehab } 19180aa77f6cSMauro Carvalho Chehab return r; 19190aa77f6cSMauro Carvalho Chehab } 19200aa77f6cSMauro Carvalho Chehab 19210aa77f6cSMauro Carvalho Chehab /* 19220aa77f6cSMauro Carvalho Chehab * retrieve FX2 firmware version. future use. 19230aa77f6cSMauro Carvalho Chehab * @param dev pointer to device extension 19240aa77f6cSMauro Carvalho Chehab * @return -1 for fail, else returns firmware version as an int(16 bits) 19250aa77f6cSMauro Carvalho Chehab */ 19260aa77f6cSMauro Carvalho Chehab static int s2255_get_fx2fw(struct s2255_dev *dev) 19270aa77f6cSMauro Carvalho Chehab { 19280aa77f6cSMauro Carvalho Chehab int fw; 19290aa77f6cSMauro Carvalho Chehab int ret; 19300aa77f6cSMauro Carvalho Chehab unsigned char transBuffer[64]; 19310aa77f6cSMauro Carvalho Chehab ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, 19320aa77f6cSMauro Carvalho Chehab S2255_VR_IN); 19330aa77f6cSMauro Carvalho Chehab if (ret < 0) 1934f5402007Ssensoray-dev dprintk(dev, 2, "get fw error: %x\n", ret); 19350aa77f6cSMauro Carvalho Chehab fw = transBuffer[0] + (transBuffer[1] << 8); 1936f5402007Ssensoray-dev dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); 19370aa77f6cSMauro Carvalho Chehab return fw; 19380aa77f6cSMauro Carvalho Chehab } 19390aa77f6cSMauro Carvalho Chehab 19400aa77f6cSMauro Carvalho Chehab /* 19410aa77f6cSMauro Carvalho Chehab * Create the system ring buffer to copy frames into from the 19420aa77f6cSMauro Carvalho Chehab * usb read pipe. 19430aa77f6cSMauro Carvalho Chehab */ 19445e950fafSDean Anderson static int s2255_create_sys_buffers(struct s2255_vc *vc) 19450aa77f6cSMauro Carvalho Chehab { 19460aa77f6cSMauro Carvalho Chehab unsigned long i; 19470aa77f6cSMauro Carvalho Chehab unsigned long reqsize; 19485e950fafSDean Anderson vc->buffer.dwFrames = SYS_FRAMES; 19490aa77f6cSMauro Carvalho Chehab /* always allocate maximum size(PAL) for system buffers */ 19500aa77f6cSMauro Carvalho Chehab reqsize = SYS_FRAMES_MAXSIZE; 19510aa77f6cSMauro Carvalho Chehab 19520aa77f6cSMauro Carvalho Chehab if (reqsize > SYS_FRAMES_MAXSIZE) 19530aa77f6cSMauro Carvalho Chehab reqsize = SYS_FRAMES_MAXSIZE; 19540aa77f6cSMauro Carvalho Chehab 19550aa77f6cSMauro Carvalho Chehab for (i = 0; i < SYS_FRAMES; i++) { 19560aa77f6cSMauro Carvalho Chehab /* allocate the frames */ 19575e950fafSDean Anderson vc->buffer.frame[i].lpvbits = vmalloc(reqsize); 19585e950fafSDean Anderson vc->buffer.frame[i].size = reqsize; 19595e950fafSDean Anderson if (vc->buffer.frame[i].lpvbits == NULL) { 1960f5402007Ssensoray-dev pr_info("out of memory. using less frames\n"); 19615e950fafSDean Anderson vc->buffer.dwFrames = i; 19620aa77f6cSMauro Carvalho Chehab break; 19630aa77f6cSMauro Carvalho Chehab } 19640aa77f6cSMauro Carvalho Chehab } 19650aa77f6cSMauro Carvalho Chehab 19660aa77f6cSMauro Carvalho Chehab /* make sure internal states are set */ 19670aa77f6cSMauro Carvalho Chehab for (i = 0; i < SYS_FRAMES; i++) { 19685e950fafSDean Anderson vc->buffer.frame[i].ulState = 0; 19695e950fafSDean Anderson vc->buffer.frame[i].cur_size = 0; 19700aa77f6cSMauro Carvalho Chehab } 19710aa77f6cSMauro Carvalho Chehab 19725e950fafSDean Anderson vc->cur_frame = 0; 19735e950fafSDean Anderson vc->last_frame = -1; 19740aa77f6cSMauro Carvalho Chehab return 0; 19750aa77f6cSMauro Carvalho Chehab } 19760aa77f6cSMauro Carvalho Chehab 19775e950fafSDean Anderson static int s2255_release_sys_buffers(struct s2255_vc *vc) 19780aa77f6cSMauro Carvalho Chehab { 19790aa77f6cSMauro Carvalho Chehab unsigned long i; 19800aa77f6cSMauro Carvalho Chehab for (i = 0; i < SYS_FRAMES; i++) { 19815e950fafSDean Anderson if (vc->buffer.frame[i].lpvbits) 19825e950fafSDean Anderson vfree(vc->buffer.frame[i].lpvbits); 19835e950fafSDean Anderson vc->buffer.frame[i].lpvbits = NULL; 19840aa77f6cSMauro Carvalho Chehab } 19850aa77f6cSMauro Carvalho Chehab return 0; 19860aa77f6cSMauro Carvalho Chehab } 19870aa77f6cSMauro Carvalho Chehab 19880aa77f6cSMauro Carvalho Chehab static int s2255_board_init(struct s2255_dev *dev) 19890aa77f6cSMauro Carvalho Chehab { 19900aa77f6cSMauro Carvalho Chehab struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; 19910aa77f6cSMauro Carvalho Chehab int fw_ver; 19920aa77f6cSMauro Carvalho Chehab int j; 19930aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe = &dev->pipe; 1994f5402007Ssensoray-dev dprintk(dev, 4, "board init: %p", dev); 19950aa77f6cSMauro Carvalho Chehab memset(pipe, 0, sizeof(*pipe)); 19960aa77f6cSMauro Carvalho Chehab pipe->dev = dev; 19970aa77f6cSMauro Carvalho Chehab pipe->cur_transfer_size = S2255_USB_XFER_SIZE; 19980aa77f6cSMauro Carvalho Chehab pipe->max_transfer_size = S2255_USB_XFER_SIZE; 19990aa77f6cSMauro Carvalho Chehab 20000aa77f6cSMauro Carvalho Chehab pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, 20010aa77f6cSMauro Carvalho Chehab GFP_KERNEL); 20020aa77f6cSMauro Carvalho Chehab if (pipe->transfer_buffer == NULL) { 2003f5402007Ssensoray-dev dprintk(dev, 1, "out of memory!\n"); 20040aa77f6cSMauro Carvalho Chehab return -ENOMEM; 20050aa77f6cSMauro Carvalho Chehab } 20060aa77f6cSMauro Carvalho Chehab /* query the firmware */ 20070aa77f6cSMauro Carvalho Chehab fw_ver = s2255_get_fx2fw(dev); 20080aa77f6cSMauro Carvalho Chehab 2009f5402007Ssensoray-dev pr_info("s2255: usb firmware version %d.%d\n", 20100aa77f6cSMauro Carvalho Chehab (fw_ver >> 8) & 0xff, 20110aa77f6cSMauro Carvalho Chehab fw_ver & 0xff); 20120aa77f6cSMauro Carvalho Chehab 20130aa77f6cSMauro Carvalho Chehab if (fw_ver < S2255_CUR_USB_FWVER) 2014f5402007Ssensoray-dev pr_info("s2255: newer USB firmware available\n"); 20150aa77f6cSMauro Carvalho Chehab 20160aa77f6cSMauro Carvalho Chehab for (j = 0; j < MAX_CHANNELS; j++) { 20175e950fafSDean Anderson struct s2255_vc *vc = &dev->vc[j]; 20185e950fafSDean Anderson vc->mode = mode_def; 20190aa77f6cSMauro Carvalho Chehab if (dev->pid == 0x2257 && j > 1) 20205e950fafSDean Anderson vc->mode.color |= (1 << 16); 20215e950fafSDean Anderson vc->jpegqual = S2255_DEF_JPEG_QUAL; 20225e950fafSDean Anderson vc->width = LINE_SZ_4CIFS_NTSC; 20235e950fafSDean Anderson vc->height = NUM_LINES_4CIFS_NTSC * 2; 20245e950fafSDean Anderson vc->std = V4L2_STD_NTSC_M; 20255e950fafSDean Anderson vc->fmt = &formats[0]; 20265e950fafSDean Anderson vc->mode.restart = 1; 20275e950fafSDean Anderson vc->req_image_size = get_transfer_size(&mode_def); 20285e950fafSDean Anderson vc->frame_count = 0; 20290aa77f6cSMauro Carvalho Chehab /* create the system buffers */ 20305e950fafSDean Anderson s2255_create_sys_buffers(vc); 20310aa77f6cSMauro Carvalho Chehab } 20320aa77f6cSMauro Carvalho Chehab /* start read pipe */ 20330aa77f6cSMauro Carvalho Chehab s2255_start_readpipe(dev); 2034f5402007Ssensoray-dev dprintk(dev, 1, "%s: success\n", __func__); 20350aa77f6cSMauro Carvalho Chehab return 0; 20360aa77f6cSMauro Carvalho Chehab } 20370aa77f6cSMauro Carvalho Chehab 20380aa77f6cSMauro Carvalho Chehab static int s2255_board_shutdown(struct s2255_dev *dev) 20390aa77f6cSMauro Carvalho Chehab { 20400aa77f6cSMauro Carvalho Chehab u32 i; 2041f5402007Ssensoray-dev dprintk(dev, 1, "%s: dev: %p", __func__, dev); 20420aa77f6cSMauro Carvalho Chehab 20430aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 2044340a30c5Ssensoray-dev if (vb2_is_streaming(&dev->vc[i].vb_vidq)) 20455e950fafSDean Anderson s2255_stop_acquire(&dev->vc[i]); 20460aa77f6cSMauro Carvalho Chehab } 20470aa77f6cSMauro Carvalho Chehab s2255_stop_readpipe(dev); 20480aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) 20495e950fafSDean Anderson s2255_release_sys_buffers(&dev->vc[i]); 20500aa77f6cSMauro Carvalho Chehab /* release transfer buffer */ 20510aa77f6cSMauro Carvalho Chehab kfree(dev->pipe.transfer_buffer); 20520aa77f6cSMauro Carvalho Chehab return 0; 20530aa77f6cSMauro Carvalho Chehab } 20540aa77f6cSMauro Carvalho Chehab 20550aa77f6cSMauro Carvalho Chehab static void read_pipe_completion(struct urb *purb) 20560aa77f6cSMauro Carvalho Chehab { 20570aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe_info; 20580aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev; 20590aa77f6cSMauro Carvalho Chehab int status; 20600aa77f6cSMauro Carvalho Chehab int pipe; 20610aa77f6cSMauro Carvalho Chehab pipe_info = purb->context; 20620aa77f6cSMauro Carvalho Chehab if (pipe_info == NULL) { 20630aa77f6cSMauro Carvalho Chehab dev_err(&purb->dev->dev, "no context!\n"); 20640aa77f6cSMauro Carvalho Chehab return; 20650aa77f6cSMauro Carvalho Chehab } 20660aa77f6cSMauro Carvalho Chehab dev = pipe_info->dev; 20670aa77f6cSMauro Carvalho Chehab if (dev == NULL) { 20680aa77f6cSMauro Carvalho Chehab dev_err(&purb->dev->dev, "no context!\n"); 20690aa77f6cSMauro Carvalho Chehab return; 20700aa77f6cSMauro Carvalho Chehab } 20710aa77f6cSMauro Carvalho Chehab status = purb->status; 20720aa77f6cSMauro Carvalho Chehab /* if shutting down, do not resubmit, exit immediately */ 20730aa77f6cSMauro Carvalho Chehab if (status == -ESHUTDOWN) { 2074f5402007Ssensoray-dev dprintk(dev, 2, "%s: err shutdown\n", __func__); 20750aa77f6cSMauro Carvalho Chehab pipe_info->err_count++; 20760aa77f6cSMauro Carvalho Chehab return; 20770aa77f6cSMauro Carvalho Chehab } 20780aa77f6cSMauro Carvalho Chehab 20790aa77f6cSMauro Carvalho Chehab if (pipe_info->state == 0) { 2080f5402007Ssensoray-dev dprintk(dev, 2, "%s: exiting USB pipe", __func__); 20810aa77f6cSMauro Carvalho Chehab return; 20820aa77f6cSMauro Carvalho Chehab } 20830aa77f6cSMauro Carvalho Chehab 20840aa77f6cSMauro Carvalho Chehab if (status == 0) 20850aa77f6cSMauro Carvalho Chehab s2255_read_video_callback(dev, pipe_info); 20860aa77f6cSMauro Carvalho Chehab else { 20870aa77f6cSMauro Carvalho Chehab pipe_info->err_count++; 2088f5402007Ssensoray-dev dprintk(dev, 1, "%s: failed URB %d\n", __func__, status); 20890aa77f6cSMauro Carvalho Chehab } 20900aa77f6cSMauro Carvalho Chehab 20910aa77f6cSMauro Carvalho Chehab pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); 20920aa77f6cSMauro Carvalho Chehab /* reuse urb */ 20930aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, 20940aa77f6cSMauro Carvalho Chehab pipe, 20950aa77f6cSMauro Carvalho Chehab pipe_info->transfer_buffer, 20960aa77f6cSMauro Carvalho Chehab pipe_info->cur_transfer_size, 20970aa77f6cSMauro Carvalho Chehab read_pipe_completion, pipe_info); 20980aa77f6cSMauro Carvalho Chehab 20990aa77f6cSMauro Carvalho Chehab if (pipe_info->state != 0) { 2100f5402007Ssensoray-dev if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) 21010aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "error submitting urb\n"); 21020aa77f6cSMauro Carvalho Chehab } else { 2103f5402007Ssensoray-dev dprintk(dev, 2, "%s :complete state 0\n", __func__); 21040aa77f6cSMauro Carvalho Chehab } 21050aa77f6cSMauro Carvalho Chehab return; 21060aa77f6cSMauro Carvalho Chehab } 21070aa77f6cSMauro Carvalho Chehab 21080aa77f6cSMauro Carvalho Chehab static int s2255_start_readpipe(struct s2255_dev *dev) 21090aa77f6cSMauro Carvalho Chehab { 21100aa77f6cSMauro Carvalho Chehab int pipe; 21110aa77f6cSMauro Carvalho Chehab int retval; 21120aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe_info = &dev->pipe; 21130aa77f6cSMauro Carvalho Chehab pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); 2114f5402007Ssensoray-dev dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint); 21150aa77f6cSMauro Carvalho Chehab pipe_info->state = 1; 21160aa77f6cSMauro Carvalho Chehab pipe_info->err_count = 0; 21170aa77f6cSMauro Carvalho Chehab pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); 21180aa77f6cSMauro Carvalho Chehab if (!pipe_info->stream_urb) { 21190aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, 21200aa77f6cSMauro Carvalho Chehab "ReadStream: Unable to alloc URB\n"); 21210aa77f6cSMauro Carvalho Chehab return -ENOMEM; 21220aa77f6cSMauro Carvalho Chehab } 21230aa77f6cSMauro Carvalho Chehab /* transfer buffer allocated in board_init */ 21240aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, 21250aa77f6cSMauro Carvalho Chehab pipe, 21260aa77f6cSMauro Carvalho Chehab pipe_info->transfer_buffer, 21270aa77f6cSMauro Carvalho Chehab pipe_info->cur_transfer_size, 21280aa77f6cSMauro Carvalho Chehab read_pipe_completion, pipe_info); 21290aa77f6cSMauro Carvalho Chehab retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); 21300aa77f6cSMauro Carvalho Chehab if (retval) { 2131f5402007Ssensoray-dev pr_err("s2255: start read pipe failed\n"); 21320aa77f6cSMauro Carvalho Chehab return retval; 21330aa77f6cSMauro Carvalho Chehab } 21340aa77f6cSMauro Carvalho Chehab return 0; 21350aa77f6cSMauro Carvalho Chehab } 21360aa77f6cSMauro Carvalho Chehab 21370aa77f6cSMauro Carvalho Chehab /* starts acquisition process */ 21385e950fafSDean Anderson static int s2255_start_acquire(struct s2255_vc *vc) 21390aa77f6cSMauro Carvalho Chehab { 21400aa77f6cSMauro Carvalho Chehab int res; 21410aa77f6cSMauro Carvalho Chehab unsigned long chn_rev; 21420aa77f6cSMauro Carvalho Chehab int j; 21435e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 214447d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 21450aa77f6cSMauro Carvalho Chehab 214647d8c881SDean Anderson mutex_lock(&dev->cmdlock); 214747d8c881SDean Anderson chn_rev = G_chnmap[vc->idx]; 21485e950fafSDean Anderson vc->last_frame = -1; 21495e950fafSDean Anderson vc->bad_payload = 0; 21505e950fafSDean Anderson vc->cur_frame = 0; 21510aa77f6cSMauro Carvalho Chehab for (j = 0; j < SYS_FRAMES; j++) { 21525e950fafSDean Anderson vc->buffer.frame[j].ulState = 0; 21535e950fafSDean Anderson vc->buffer.frame[j].cur_size = 0; 21540aa77f6cSMauro Carvalho Chehab } 21550aa77f6cSMauro Carvalho Chehab 21560aa77f6cSMauro Carvalho Chehab /* send the start command */ 215747d8c881SDean Anderson buffer[0] = IN_DATA_TOKEN; 215847d8c881SDean Anderson buffer[1] = (__le32) cpu_to_le32(chn_rev); 215947d8c881SDean Anderson buffer[2] = CMD_START; 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_START error\n"); 21630aa77f6cSMauro Carvalho Chehab 21645e950fafSDean Anderson dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res); 216547d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 21666a5b63b3SDean Anderson return res; 21670aa77f6cSMauro Carvalho Chehab } 21680aa77f6cSMauro Carvalho Chehab 21695e950fafSDean Anderson static int s2255_stop_acquire(struct s2255_vc *vc) 21700aa77f6cSMauro Carvalho Chehab { 21710aa77f6cSMauro Carvalho Chehab int res; 21720aa77f6cSMauro Carvalho Chehab unsigned long chn_rev; 21735e950fafSDean Anderson struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev); 217447d8c881SDean Anderson __le32 *buffer = dev->cmdbuf; 217547d8c881SDean Anderson 217647d8c881SDean Anderson mutex_lock(&dev->cmdlock); 21775e950fafSDean Anderson chn_rev = G_chnmap[vc->idx]; 21780aa77f6cSMauro Carvalho Chehab buffer = kzalloc(512, GFP_KERNEL); 21790aa77f6cSMauro Carvalho Chehab if (buffer == NULL) { 21800aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "out of mem\n"); 21810aa77f6cSMauro Carvalho Chehab return -ENOMEM; 21820aa77f6cSMauro Carvalho Chehab } 21830aa77f6cSMauro Carvalho Chehab /* send the stop command */ 218447d8c881SDean Anderson buffer[0] = IN_DATA_TOKEN; 218547d8c881SDean Anderson buffer[1] = (__le32) cpu_to_le32(chn_rev); 218647d8c881SDean Anderson buffer[2] = CMD_STOP; 218747d8c881SDean Anderson 21880aa77f6cSMauro Carvalho Chehab res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); 21890aa77f6cSMauro Carvalho Chehab if (res != 0) 21900aa77f6cSMauro Carvalho Chehab dev_err(&dev->udev->dev, "CMD_STOP error\n"); 219147d8c881SDean Anderson 21925e950fafSDean Anderson dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res); 219347d8c881SDean Anderson mutex_unlock(&dev->cmdlock); 21940aa77f6cSMauro Carvalho Chehab return res; 21950aa77f6cSMauro Carvalho Chehab } 21960aa77f6cSMauro Carvalho Chehab 21970aa77f6cSMauro Carvalho Chehab static void s2255_stop_readpipe(struct s2255_dev *dev) 21980aa77f6cSMauro Carvalho Chehab { 21990aa77f6cSMauro Carvalho Chehab struct s2255_pipeinfo *pipe = &dev->pipe; 22000aa77f6cSMauro Carvalho Chehab 22010aa77f6cSMauro Carvalho Chehab pipe->state = 0; 22020aa77f6cSMauro Carvalho Chehab if (pipe->stream_urb) { 22030aa77f6cSMauro Carvalho Chehab /* cancel urb */ 22040aa77f6cSMauro Carvalho Chehab usb_kill_urb(pipe->stream_urb); 22050aa77f6cSMauro Carvalho Chehab usb_free_urb(pipe->stream_urb); 22060aa77f6cSMauro Carvalho Chehab pipe->stream_urb = NULL; 22070aa77f6cSMauro Carvalho Chehab } 2208f5402007Ssensoray-dev dprintk(dev, 4, "%s", __func__); 22090aa77f6cSMauro Carvalho Chehab return; 22100aa77f6cSMauro Carvalho Chehab } 22110aa77f6cSMauro Carvalho Chehab 22120aa77f6cSMauro Carvalho Chehab static void s2255_fwload_start(struct s2255_dev *dev, int reset) 22130aa77f6cSMauro Carvalho Chehab { 22140aa77f6cSMauro Carvalho Chehab if (reset) 22150aa77f6cSMauro Carvalho Chehab s2255_reset_dsppower(dev); 22160aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_size = dev->fw_data->fw->size; 22170aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); 22180aa77f6cSMauro Carvalho Chehab memcpy(dev->fw_data->pfw_data, 22190aa77f6cSMauro Carvalho Chehab dev->fw_data->fw->data, CHUNK_SIZE); 22200aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_loaded = CHUNK_SIZE; 22210aa77f6cSMauro Carvalho Chehab usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, 22220aa77f6cSMauro Carvalho Chehab usb_sndbulkpipe(dev->udev, 2), 22230aa77f6cSMauro Carvalho Chehab dev->fw_data->pfw_data, 22240aa77f6cSMauro Carvalho Chehab CHUNK_SIZE, s2255_fwchunk_complete, 22250aa77f6cSMauro Carvalho Chehab dev->fw_data); 22260aa77f6cSMauro Carvalho Chehab mod_timer(&dev->timer, jiffies + HZ); 22270aa77f6cSMauro Carvalho Chehab } 22280aa77f6cSMauro Carvalho Chehab 22290aa77f6cSMauro Carvalho Chehab /* standard usb probe function */ 22300aa77f6cSMauro Carvalho Chehab static int s2255_probe(struct usb_interface *interface, 22310aa77f6cSMauro Carvalho Chehab const struct usb_device_id *id) 22320aa77f6cSMauro Carvalho Chehab { 22330aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev = NULL; 22340aa77f6cSMauro Carvalho Chehab struct usb_host_interface *iface_desc; 22350aa77f6cSMauro Carvalho Chehab struct usb_endpoint_descriptor *endpoint; 22360aa77f6cSMauro Carvalho Chehab int i; 22370aa77f6cSMauro Carvalho Chehab int retval = -ENOMEM; 22380aa77f6cSMauro Carvalho Chehab __le32 *pdata; 22390aa77f6cSMauro Carvalho Chehab int fw_size; 224047d8c881SDean Anderson 22410aa77f6cSMauro Carvalho Chehab /* allocate memory for our device state and initialize it to zero */ 22420aa77f6cSMauro Carvalho Chehab dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); 22430aa77f6cSMauro Carvalho Chehab if (dev == NULL) { 22440aa77f6cSMauro Carvalho Chehab s2255_dev_err(&interface->dev, "out of memory\n"); 22450aa77f6cSMauro Carvalho Chehab return -ENOMEM; 22460aa77f6cSMauro Carvalho Chehab } 224747d8c881SDean Anderson 224847d8c881SDean Anderson dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); 224947d8c881SDean Anderson if (dev->cmdbuf == NULL) { 225047d8c881SDean Anderson s2255_dev_err(&interface->dev, "out of memory\n"); 225147d8c881SDean Anderson return -ENOMEM; 225247d8c881SDean Anderson } 225347d8c881SDean Anderson 22540aa77f6cSMauro Carvalho Chehab atomic_set(&dev->num_channels, 0); 22550b84caabSHans Verkuil dev->pid = le16_to_cpu(id->idProduct); 22560aa77f6cSMauro Carvalho Chehab dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); 22570aa77f6cSMauro Carvalho Chehab if (!dev->fw_data) 22580aa77f6cSMauro Carvalho Chehab goto errorFWDATA1; 22590aa77f6cSMauro Carvalho Chehab mutex_init(&dev->lock); 226047d8c881SDean Anderson mutex_init(&dev->cmdlock); 22610aa77f6cSMauro Carvalho Chehab /* grab usb_device and save it */ 22620aa77f6cSMauro Carvalho Chehab dev->udev = usb_get_dev(interface_to_usbdev(interface)); 22630aa77f6cSMauro Carvalho Chehab if (dev->udev == NULL) { 22640aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "null usb device\n"); 22650aa77f6cSMauro Carvalho Chehab retval = -ENODEV; 22660aa77f6cSMauro Carvalho Chehab goto errorUDEV; 22670aa77f6cSMauro Carvalho Chehab } 2268f5402007Ssensoray-dev dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n", 2269f5402007Ssensoray-dev dev, dev->udev, interface); 22700aa77f6cSMauro Carvalho Chehab dev->interface = interface; 22710aa77f6cSMauro Carvalho Chehab /* set up the endpoint information */ 22720aa77f6cSMauro Carvalho Chehab iface_desc = interface->cur_altsetting; 2273f5402007Ssensoray-dev dev_dbg(&interface->dev, "num EP: %d\n", 2274f5402007Ssensoray-dev iface_desc->desc.bNumEndpoints); 22750aa77f6cSMauro Carvalho Chehab for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 22760aa77f6cSMauro Carvalho Chehab endpoint = &iface_desc->endpoint[i].desc; 22770aa77f6cSMauro Carvalho Chehab if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { 22780aa77f6cSMauro Carvalho Chehab /* we found the bulk in endpoint */ 22790aa77f6cSMauro Carvalho Chehab dev->read_endpoint = endpoint->bEndpointAddress; 22800aa77f6cSMauro Carvalho Chehab } 22810aa77f6cSMauro Carvalho Chehab } 22820aa77f6cSMauro Carvalho Chehab 22830aa77f6cSMauro Carvalho Chehab if (!dev->read_endpoint) { 22840aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); 22850aa77f6cSMauro Carvalho Chehab goto errorEP; 22860aa77f6cSMauro Carvalho Chehab } 22870aa77f6cSMauro Carvalho Chehab init_timer(&dev->timer); 22880aa77f6cSMauro Carvalho Chehab dev->timer.function = s2255_timer; 22890aa77f6cSMauro Carvalho Chehab dev->timer.data = (unsigned long)dev->fw_data; 22900aa77f6cSMauro Carvalho Chehab init_waitqueue_head(&dev->fw_data->wait_fw); 22910aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 22925e950fafSDean Anderson struct s2255_vc *vc = &dev->vc[i]; 22935e950fafSDean Anderson vc->idx = i; 22945e950fafSDean Anderson vc->dev = dev; 22955e950fafSDean Anderson init_waitqueue_head(&vc->wait_setmode); 22965e950fafSDean Anderson init_waitqueue_head(&vc->wait_vidstatus); 2297340a30c5Ssensoray-dev spin_lock_init(&vc->qlock); 2298340a30c5Ssensoray-dev mutex_init(&vc->vb_lock); 22990aa77f6cSMauro Carvalho Chehab } 23000aa77f6cSMauro Carvalho Chehab 23010aa77f6cSMauro Carvalho Chehab dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); 23020aa77f6cSMauro Carvalho Chehab if (!dev->fw_data->fw_urb) { 23030aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "out of memory!\n"); 23040aa77f6cSMauro Carvalho Chehab goto errorFWURB; 23050aa77f6cSMauro Carvalho Chehab } 23060aa77f6cSMauro Carvalho Chehab 23070aa77f6cSMauro Carvalho Chehab dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); 23080aa77f6cSMauro Carvalho Chehab if (!dev->fw_data->pfw_data) { 23090aa77f6cSMauro Carvalho Chehab dev_err(&interface->dev, "out of memory!\n"); 23100aa77f6cSMauro Carvalho Chehab goto errorFWDATA2; 23110aa77f6cSMauro Carvalho Chehab } 23120aa77f6cSMauro Carvalho Chehab /* load the first chunk */ 23130aa77f6cSMauro Carvalho Chehab if (request_firmware(&dev->fw_data->fw, 23140aa77f6cSMauro Carvalho Chehab FIRMWARE_FILE_NAME, &dev->udev->dev)) { 2315f5402007Ssensoray-dev dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n"); 23160aa77f6cSMauro Carvalho Chehab goto errorREQFW; 23170aa77f6cSMauro Carvalho Chehab } 23180aa77f6cSMauro Carvalho Chehab /* check the firmware is valid */ 23190aa77f6cSMauro Carvalho Chehab fw_size = dev->fw_data->fw->size; 23200aa77f6cSMauro Carvalho Chehab pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; 23210aa77f6cSMauro Carvalho Chehab 23220aa77f6cSMauro Carvalho Chehab if (*pdata != S2255_FW_MARKER) { 2323f5402007Ssensoray-dev dev_err(&interface->dev, "Firmware invalid.\n"); 23240aa77f6cSMauro Carvalho Chehab retval = -ENODEV; 23250aa77f6cSMauro Carvalho Chehab goto errorFWMARKER; 23260aa77f6cSMauro Carvalho Chehab } else { 23270aa77f6cSMauro Carvalho Chehab /* make sure firmware is the latest */ 23280aa77f6cSMauro Carvalho Chehab __le32 *pRel; 23290aa77f6cSMauro Carvalho Chehab pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; 2330f5402007Ssensoray-dev pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel)); 23310aa77f6cSMauro Carvalho Chehab dev->dsp_fw_ver = le32_to_cpu(*pRel); 23320aa77f6cSMauro Carvalho Chehab if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) 2333f5402007Ssensoray-dev pr_info("s2255: f2255usb.bin out of date.\n"); 23340aa77f6cSMauro Carvalho Chehab if (dev->pid == 0x2257 && 23350aa77f6cSMauro Carvalho Chehab dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) 2336f5402007Ssensoray-dev pr_warn("2257 needs firmware %d or above.\n", 2337f5402007Ssensoray-dev S2255_MIN_DSP_COLORFILTER); 23380aa77f6cSMauro Carvalho Chehab } 23390aa77f6cSMauro Carvalho Chehab usb_reset_device(dev->udev); 23400aa77f6cSMauro Carvalho Chehab /* load 2255 board specific */ 23410aa77f6cSMauro Carvalho Chehab retval = s2255_board_init(dev); 23420aa77f6cSMauro Carvalho Chehab if (retval) 23430aa77f6cSMauro Carvalho Chehab goto errorBOARDINIT; 23440aa77f6cSMauro Carvalho Chehab s2255_fwload_start(dev, 0); 23450aa77f6cSMauro Carvalho Chehab /* loads v4l specific */ 23460aa77f6cSMauro Carvalho Chehab retval = s2255_probe_v4l(dev); 23470aa77f6cSMauro Carvalho Chehab if (retval) 23480aa77f6cSMauro Carvalho Chehab goto errorBOARDINIT; 23490aa77f6cSMauro Carvalho Chehab dev_info(&interface->dev, "Sensoray 2255 detected\n"); 23500aa77f6cSMauro Carvalho Chehab return 0; 23510aa77f6cSMauro Carvalho Chehab errorBOARDINIT: 23520aa77f6cSMauro Carvalho Chehab s2255_board_shutdown(dev); 23530aa77f6cSMauro Carvalho Chehab errorFWMARKER: 23540aa77f6cSMauro Carvalho Chehab release_firmware(dev->fw_data->fw); 23550aa77f6cSMauro Carvalho Chehab errorREQFW: 23560aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data->pfw_data); 23570aa77f6cSMauro Carvalho Chehab errorFWDATA2: 23580aa77f6cSMauro Carvalho Chehab usb_free_urb(dev->fw_data->fw_urb); 23590aa77f6cSMauro Carvalho Chehab errorFWURB: 23600aa77f6cSMauro Carvalho Chehab del_timer(&dev->timer); 23610aa77f6cSMauro Carvalho Chehab errorEP: 23620aa77f6cSMauro Carvalho Chehab usb_put_dev(dev->udev); 23630aa77f6cSMauro Carvalho Chehab errorUDEV: 23640aa77f6cSMauro Carvalho Chehab kfree(dev->fw_data); 23650aa77f6cSMauro Carvalho Chehab mutex_destroy(&dev->lock); 23660aa77f6cSMauro Carvalho Chehab errorFWDATA1: 236747d8c881SDean Anderson kfree(dev->cmdbuf); 23680aa77f6cSMauro Carvalho Chehab kfree(dev); 2369f5402007Ssensoray-dev pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval); 23700aa77f6cSMauro Carvalho Chehab return retval; 23710aa77f6cSMauro Carvalho Chehab } 23720aa77f6cSMauro Carvalho Chehab 23730aa77f6cSMauro Carvalho Chehab /* disconnect routine. when board is removed physically or with rmmod */ 23740aa77f6cSMauro Carvalho Chehab static void s2255_disconnect(struct usb_interface *interface) 23750aa77f6cSMauro Carvalho Chehab { 23760aa77f6cSMauro Carvalho Chehab struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); 23770aa77f6cSMauro Carvalho Chehab int i; 23780aa77f6cSMauro Carvalho Chehab int channels = atomic_read(&dev->num_channels); 23790aa77f6cSMauro Carvalho Chehab mutex_lock(&dev->lock); 23800aa77f6cSMauro Carvalho Chehab v4l2_device_disconnect(&dev->v4l2_dev); 23810aa77f6cSMauro Carvalho Chehab mutex_unlock(&dev->lock); 23820aa77f6cSMauro Carvalho Chehab /*see comments in the uvc_driver.c usb disconnect function */ 23830aa77f6cSMauro Carvalho Chehab atomic_inc(&dev->num_channels); 23840aa77f6cSMauro Carvalho Chehab /* unregister each video device. */ 23850aa77f6cSMauro Carvalho Chehab for (i = 0; i < channels; i++) 23865e950fafSDean Anderson video_unregister_device(&dev->vc[i].vdev); 23870aa77f6cSMauro Carvalho Chehab /* wake up any of our timers */ 23880aa77f6cSMauro Carvalho Chehab atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); 23890aa77f6cSMauro Carvalho Chehab wake_up(&dev->fw_data->wait_fw); 23900aa77f6cSMauro Carvalho Chehab for (i = 0; i < MAX_CHANNELS; i++) { 23915e950fafSDean Anderson dev->vc[i].setmode_ready = 1; 23925e950fafSDean Anderson wake_up(&dev->vc[i].wait_setmode); 23935e950fafSDean Anderson dev->vc[i].vidstatus_ready = 1; 23945e950fafSDean Anderson wake_up(&dev->vc[i].wait_vidstatus); 23950aa77f6cSMauro Carvalho Chehab } 23960aa77f6cSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->num_channels)) 23970aa77f6cSMauro Carvalho Chehab s2255_destroy(dev); 23980aa77f6cSMauro Carvalho Chehab dev_info(&interface->dev, "%s\n", __func__); 23990aa77f6cSMauro Carvalho Chehab } 24000aa77f6cSMauro Carvalho Chehab 24010aa77f6cSMauro Carvalho Chehab static struct usb_driver s2255_driver = { 24020aa77f6cSMauro Carvalho Chehab .name = S2255_DRIVER_NAME, 24030aa77f6cSMauro Carvalho Chehab .probe = s2255_probe, 24040aa77f6cSMauro Carvalho Chehab .disconnect = s2255_disconnect, 24050aa77f6cSMauro Carvalho Chehab .id_table = s2255_table, 24060aa77f6cSMauro Carvalho Chehab }; 24070aa77f6cSMauro Carvalho Chehab 24080aa77f6cSMauro Carvalho Chehab module_usb_driver(s2255_driver); 24090aa77f6cSMauro Carvalho Chehab 24100aa77f6cSMauro Carvalho Chehab MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); 24110aa77f6cSMauro Carvalho Chehab MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); 24120aa77f6cSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 24130aa77f6cSMauro Carvalho Chehab MODULE_VERSION(S2255_VERSION); 24140aa77f6cSMauro Carvalho Chehab MODULE_FIRMWARE(FIRMWARE_FILE_NAME); 2415