diff options
author | Len Brown <len.brown@intel.com> | 2010-08-15 01:06:31 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2010-08-15 01:06:31 -0400 |
commit | 95ee46aa8698f2000647dfb362400fadbb5807cf (patch) | |
tree | e5a05c7297f997e191c73091934e42e3195c0e40 /drivers/media/video | |
parent | cfa806f059801dbe7e435745eb2e187c8bfe1e7f (diff) | |
parent | 92fa5bd9a946b6e7aab6764e7312e4e3d9bed295 (diff) |
Merge branch 'linus' into release
Conflicts:
drivers/acpi/debug.c
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/media/video')
198 files changed, 17938 insertions, 24973 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index bdbc9d30541..f6e4d047535 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -83,7 +83,7 @@ config VIDEO_FIXED_MINOR_RANGES config VIDEO_HELPER_CHIPS_AUTO bool "Autoselect pertinent encoders/decoders and other helper chips" - default y + default y if !EMBEDDED ---help--- Most video cards may require additional modules to encode or decode audio/video standards. This option will autoselect @@ -517,19 +517,6 @@ config VIDEO_UPD64083 endmenu # encoder / decoder chips -config DISPLAY_DAVINCI_DM646X_EVM - tristate "DM646x EVM Video Display" - depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM - select VIDEOBUF_DMA_CONTIG - select VIDEO_DAVINCI_VPIF - select VIDEO_ADV7343 - select VIDEO_THS7303 - help - Support for DM6467 based display device. - - To compile this driver as a module, choose M here: the - module will be called vpif_display. - config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on VIDEO_DEV && ARCH_SHMOBILE @@ -537,29 +524,22 @@ config VIDEO_SH_VOU help Support for the Video Output Unit (VOU) on SuperH SoCs. -config CAPTURE_DAVINCI_DM646X_EVM - tristate "DM646x EVM Video Capture" - depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM +config VIDEO_VIU + tristate "Freescale VIU Video Driver" + depends on VIDEO_V4L2 && PPC_MPC512x select VIDEOBUF_DMA_CONTIG - select VIDEO_DAVINCI_VPIF - help - Support for DM6467 based capture device. - - To compile this driver as a module, choose M here: the - module will be called vpif_capture. - -config VIDEO_DAVINCI_VPIF - tristate "DaVinci VPIF Driver" - depends on DISPLAY_DAVINCI_DM646X_EVM - help - Support for DaVinci VPIF Driver. + default y + ---help--- + Support for Freescale VIU video driver. This device captures + video data, or overlays video on DIU frame buffer. - To compile this driver as a module, choose M here: the - module will be called vpif. + Say Y here if you want to enable VIU device on MPC5121e Rev2+. + In doubt, say N. config VIDEO_VIVI tristate "Virtual Video Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FONTS + depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + depends on (FRAMEBUFFER_CONSOLE || STI_CONSOLE) && FONTS select FONT_8x16 select VIDEOBUF_VMALLOC default n @@ -570,66 +550,7 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. -config VIDEO_VPSS_SYSTEM - tristate "VPSS System module driver" - depends on ARCH_DAVINCI - help - Support for vpss system module for video driver - -config VIDEO_VPFE_CAPTURE - tristate "VPFE Video Capture Driver" - depends on VIDEO_V4L2 && ARCH_DAVINCI - select VIDEOBUF_DMA_CONTIG - help - Support for DMXXXX VPFE based frame grabber. This is the - common V4L2 module for following DMXXX SoCs from Texas - Instruments:- DM6446 & DM355. - - To compile this driver as a module, choose M here: the - module will be called vpfe-capture. - -config VIDEO_DM6446_CCDC - tristate "DM6446 CCDC HW module" - depends on ARCH_DAVINCI_DM644x && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help - Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces - with decoder modules such as TVP5146 over BT656 or - sensor module such as MT9T001 over a raw interface. This - module configures the interface and CCDC/ISIF to do - video frame capture from slave decoders. - - To compile this driver as a module, choose M here: the - module will be called vpfe. - -config VIDEO_DM355_CCDC - tristate "DM355 CCDC HW module" - depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help - Enables DM355 CCD hw module. DM355 CCDC hw interfaces - with decoder modules such as TVP5146 over BT656 or - sensor module such as MT9T001 over a raw interface. This - module configures the interface and CCDC/ISIF to do - video frame capture from a slave decoders - - To compile this driver as a module, choose M here: the - module will be called vpfe. - -config VIDEO_ISIF - tristate "ISIF HW module" - depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help - Enables ISIF hw module. This is the hardware module for - configuring ISIF in VPFE to capture Raw Bayer RGB data from - a image sensor or YUV data from a YUV source. - - To compile this driver as a module, choose M here: the - module will be called vpfe. +source "drivers/media/video/davinci/Kconfig" source "drivers/media/video/omap/Kconfig" @@ -871,10 +792,11 @@ config SOC_CAMERA_MT9M001 and colour models. config SOC_CAMERA_MT9M111 - tristate "mt9m111 and mt9m112 support" + tristate "mt9m111, mt9m112 and mt9m131 support" depends on SOC_CAMERA && I2C help - This driver supports MT9M111 and MT9M112 cameras from Micron + This driver supports MT9M111, MT9M112 and MT9M131 cameras from + Micron/Aptina config SOC_CAMERA_MT9T031 tristate "mt9t031 support" @@ -955,6 +877,12 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface +config VIDEO_SH_MOBILE_CSI2 + tristate "SuperH Mobile MIPI CSI-2 Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK + ---help--- + This is a v4l2 driver for the SuperH MIPI CSI-2 Interface + config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK @@ -969,6 +897,19 @@ config VIDEO_OMAP2 ---help--- This is a v4l2 driver for the TI OMAP2 camera capture interface +config VIDEO_MX2_HOSTSUPPORT + bool + +config VIDEO_MX2 + tristate "i.MX27/i.MX25 Camera Sensor Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25) + select VIDEOBUF_DMA_CONTIG + select VIDEO_MX2_HOSTSUPPORT + ---help--- + This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor + Interface + + # # USB Multimedia device configuration # @@ -1000,61 +941,6 @@ source "drivers/media/video/usbvideo/Kconfig" source "drivers/media/video/et61x251/Kconfig" -config VIDEO_OVCAMCHIP - tristate "OmniVision Camera Chip support (DEPRECATED)" - depends on I2C && VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca ov519 module - instead. Note that for the ov511 / ov518 support of the gspca module - you need atleast version 0.6.0 of libv4l and for the w9968cf - atleast version 0.6.3 of libv4l. - - Support for the OmniVision OV6xxx and OV7xxx series of camera chips. - This driver is intended to be used with the ov511 and w9968cf USB - camera drivers. - - To compile this driver as a module, choose M here: the - module will be called ovcamchip. - -config USB_W9968CF - tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)" - depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP - default n - ---help--- - This driver is DEPRECATED please use the gspca ov519 module - instead. Note that for the w9968cf support of the gspca module - you need atleast version 0.6.3 of libv4l. - - Say Y here if you want support for cameras based on OV681 or - Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. - - This driver has an optional plugin, which is distributed as a - separate module only (released under GPL). It allows to use higher - resolutions and framerates, but cannot be included in the official - Linux kernel for performance purposes. - - See <file:Documentation/video4linux/w9968cf.txt> for more info. - - To compile this driver as a module, choose M here: the - module will be called w9968cf. - -config USB_OV511 - tristate "USB OV511 Camera support (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca ov519 module - instead. Note that for the ov511 / ov518 support of the gspca module - you need atleast version 0.6.0 of libv4l. - - Say Y here if you want to connect this type of camera to your - computer's USB port. See <file:Documentation/video4linux/ov511.txt> - for more information and for a list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called ov511. - config USB_SE401 tristate "USB SE401 Camera support" depends on VIDEO_V4L1 @@ -1068,25 +954,6 @@ config USB_SE401 source "drivers/media/video/sn9c102/Kconfig" -config USB_STV680 - tristate "USB STV680 (Pencam) Camera support (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca stv0680 module - instead. Note that for the gspca stv0680 module you need - atleast version 0.6.3 of libv4l. - - Say Y here if you want to connect this type of camera to your - computer's USB port. This includes the Pencam line of cameras. - See <file:Documentation/video4linux/stv680.txt> for more information - and for a list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called stv680. - -source "drivers/media/video/zc0301/Kconfig" - source "drivers/media/video/pwc/Kconfig" config USB_ZR364XX @@ -1150,4 +1017,13 @@ config VIDEO_MEM2MEM_TESTDEV This is a virtual test device for the memory-to-memory driver framework. +config VIDEO_SAMSUNG_S5P_FIMC + tristate "Samsung S5P FIMC (video postprocessor) driver" + depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P + select VIDEOBUF_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for the S5P camera interface + (video postprocessor) + endif # V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index cc93859d316..40f98fba5f8 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ - v4l2-event.o + v4l2-event.o v4l2-ctrls.o # V4L2 core modules @@ -105,7 +105,6 @@ obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ -obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o @@ -127,17 +126,13 @@ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o obj-$(CONFIG_USB_DABUSB) += dabusb.o -obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o -obj-$(CONFIG_USB_STV680) += stv680.o -obj-$(CONFIG_USB_W9968CF) += w9968cf.o obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ -obj-$(CONFIG_USB_ZC0301) += zc0301/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ @@ -151,6 +146,7 @@ obj-$(CONFIG_USB_S2255) += s2255drv.o obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ +obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ @@ -162,9 +158,12 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o +obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ @@ -177,7 +176,7 @@ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o -obj-$(CONFIG_ARCH_DAVINCI) += davinci/ +obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c index 1573392f74b..b388654d48c 100644 --- a/drivers/media/video/ak881x.c +++ b/drivers/media/video/ak881x.c @@ -126,7 +126,7 @@ static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 0, 720, 2, &mf->height, 0, ak881x->lines, 1, 0); mf->field = V4L2_FIELD_INTERLACED; - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; mf->colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; @@ -136,7 +136,7 @@ static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { if (mf->field != V4L2_FIELD_INTERLACED || - mf->code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + mf->code != V4L2_MBUS_FMT_YUYV8_2X8) return -EINVAL; return ak881x_try_g_mbus_fmt(sd, mf); @@ -148,7 +148,7 @@ static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, if (index) return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + *code = V4L2_MBUS_FMT_YUYV8_2X8; return 0; } diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile index 4d262315818..5c7f2f7d980 100644 --- a/drivers/media/video/au0828/Makefile +++ b/drivers/media/video/au0828/Makefile @@ -1,4 +1,4 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/video/au0828/au0828-vbi.c b/drivers/media/video/au0828/au0828-vbi.c new file mode 100644 index 00000000000..63f593070ee --- /dev/null +++ b/drivers/media/video/au0828/au0828-vbi.c @@ -0,0 +1,138 @@ +/* + au0828-vbi.c - VBI driver for au0828 + + Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> + + This work was sponsored by GetWellNetwork Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include "au0828.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct au0828_fh *fh = q->priv_data; + struct au0828_dev *dev = fh->dev; + + *size = dev->vbi_width * dev->vbi_height * 2; + + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct au0828_fh *fh = q->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + int rc = 0; + + buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->vbi_width; + buf->vb.height = dev->vbi_height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops au0828_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 52f25aabb6d..7989a7ba7c4 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -314,6 +314,23 @@ static inline void buffer_filled(struct au0828_dev *dev, wake_up(&buf->vb.done); } +static inline void vbi_buffer_filled(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf) +{ + /* Advice that buffer was filled */ + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + /* * Identify the buffer header type and properly handles */ @@ -327,6 +344,9 @@ static void au0828_copy_video(struct au0828_dev *dev, int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */ + if (len == 0) + return; + if (dma_q->pos + len > buf->vb.size) len = buf->vb.size - dma_q->pos; @@ -414,17 +434,98 @@ static inline void get_next_buf(struct au0828_dmaqueue *dma_q, return; } +static void au0828_copy_vbi(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + unsigned char *startwrite, *startread; + int bytesperline; + int i, j = 0; + + if (dev == NULL) { + au0828_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + au0828_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) + return; + if (p == NULL) { + au0828_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + au0828_isocdbg("outp is null\n"); + return; + } + + bytesperline = dev->vbi_width; + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + startwrite = outp + (dma_q->pos / 2); + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) + startwrite += bytesperline * dev->vbi_height; + + for (i = 0; i < len; i += 2) + startwrite[j++] = startread[i+1]; + + dma_q->pos += len; +} + + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, + struct au0828_buffer **buf) +{ + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq); + char *outp; + + if (list_empty(&dma_q->active)) { + au0828_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); + + dev->isoc_ctl.vbi_buf = *buf; + + return; +} + /* * Controls the isoc copy of each urb packet */ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) { struct au0828_buffer *buf; + struct au0828_buffer *vbi_buf; struct au0828_dmaqueue *dma_q = urb->context; + struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq; unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; int i, len = 0, rc = 1; unsigned char *p; unsigned char fbyte; + unsigned int vbi_field_size; + unsigned int remain, lencopy; if (!dev) return 0; @@ -443,6 +544,10 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -472,6 +577,19 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) au0828_isocdbg("Video frame %s\n", (fbyte & 0x40) ? "odd" : "even"); if (!(fbyte & 0x40)) { + /* VBI */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + + /* Video */ if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -488,9 +606,36 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) buf->top_field = 0; } + if (vbi_buf != NULL) { + if (fbyte & 0x40) + vbi_buf->top_field = 1; + else + vbi_buf->top_field = 0; + } + + dev->vbi_read = 0; + vbi_dma_q->pos = 0; dma_q->pos = 0; } - if (buf != NULL) + + vbi_field_size = dev->vbi_width * dev->vbi_height * 2; + if (dev->vbi_read < vbi_field_size) { + remain = vbi_field_size - dev->vbi_read; + if (len < remain) + lencopy = len; + else + lencopy = remain; + + if (vbi_buf != NULL) + au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + + len -= lencopy; + p += lencopy; + dev->vbi_read += lencopy; + } + + if (dev->vbi_read >= vbi_field_size && buf != NULL) au0828_copy_video(dev, dma_q, buf, p, outp, len); } return rc; @@ -642,7 +787,7 @@ int au0828_analog_stream_enable(struct au0828_dev *d) au0828_writereg(d, 0x114, 0xa0); au0828_writereg(d, 0x115, 0x05); /* set y position */ - au0828_writereg(d, 0x112, 0x02); + au0828_writereg(d, 0x112, 0x00); au0828_writereg(d, 0x113, 0x00); au0828_writereg(d, 0x116, 0xf2); au0828_writereg(d, 0x117, 0x00); @@ -703,47 +848,83 @@ void au0828_analog_unregister(struct au0828_dev *dev) /* Usage lock check functions */ -static int res_get(struct au0828_fh *fh) +static int res_get(struct au0828_fh *fh, unsigned int bit) { - struct au0828_dev *dev = fh->dev; - int rc = 0; + struct au0828_dev *dev = fh->dev; - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (dev->stream_on) - return -EBUSY; + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + dprintk(1, "res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} - dev->stream_on = 1; - fh->stream_on = 1; - return rc; +static int res_check(struct au0828_fh *fh, unsigned int bit) +{ + return fh->resources & bit; } -static int res_check(struct au0828_fh *fh) +static int res_locked(struct au0828_dev *dev, unsigned int bit) { - return fh->stream_on; + return dev->resources & bit; } -static void res_free(struct au0828_fh *fh) +static void res_free(struct au0828_fh *fh, unsigned int bits) { - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = fh->dev; - fh->stream_on = 0; - dev->stream_on = 0; + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + dprintk(1, "res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +static int get_ressource(struct au0828_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return AU0828_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return AU0828_RESOURCE_VBI; + default: + BUG(); + return 0; + } } static int au0828_v4l2_open(struct file *filp) { int ret = 0; + struct video_device *vdev = video_devdata(filp); struct au0828_dev *dev = video_drvdata(filp); struct au0828_fh *fh; - int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int type; -#ifdef VBI_IS_WORKING - if (video_devdata(filp)->vfl_type == VFL_TYPE_GRABBER) + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: type = V4L2_BUF_TYPE_VBI_CAPTURE; -#endif + break; + default: + return -EINVAL; + } fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); if (NULL == fh) { @@ -781,10 +962,21 @@ static int au0828_v4l2_open(struct file *filp) dev->users++; videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, - NULL, &dev->slock, fh->type, + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct au0828_buffer), fh); + /* VBI Setup */ + dev->vbi_width = 720; + dev->vbi_height = 1; + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct au0828_buffer), fh); + + return ret; } @@ -794,17 +986,19 @@ static int au0828_v4l2_close(struct file *filp) struct au0828_fh *fh = filp->private_data; struct au0828_dev *dev = fh->dev; - mutex_lock(&dev->lock); - if (res_check(fh)) - res_free(fh); - - if (dev->users == 1) { + if (res_check(fh, AU0828_RESOURCE_VIDEO)) { videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); + res_free(fh, AU0828_RESOURCE_VIDEO); + } + + if (res_check(fh, AU0828_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, AU0828_RESOURCE_VBI); + } + if (dev->users == 1) { if (dev->dev_state & DEV_DISCONNECTED) { au0828_analog_unregister(dev); - mutex_unlock(&dev->lock); kfree(dev); return 0; } @@ -823,10 +1017,11 @@ static int au0828_v4l2_close(struct file *filp) printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); } + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); - mutex_unlock(&dev->lock); return 0; } @@ -842,16 +1037,21 @@ static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, return rc; if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; + if (res_locked(dev, AU0828_RESOURCE_VIDEO)) + return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); } + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VBI)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; } @@ -865,17 +1065,17 @@ static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return POLLERR; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { return POLLERR; - - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } } static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) @@ -888,14 +1088,10 @@ static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); return rc; } @@ -911,14 +1107,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, maxwidth = 720; maxheight = 480; -#ifdef VBI_IS_WORKING - if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - dprintk(1, "VBI format set: to be supported!\n"); - return 0; - } - if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE) - return 0; -#endif if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -999,9 +1187,7 @@ static int vidioc_querycap(struct file *file, void *priv, /*set the device capabilities */ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | -#ifdef VBI_IS_WORKING V4L2_CAP_VBI_CAPTURE | -#endif V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | @@ -1056,20 +1242,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct au0828_dev *dev = fh->dev; int rc; + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + if (videobuf_queue_is_busy(&fh->vb_vidq)) { printk(KERN_INFO "%s queue busy\n", __func__); rc = -EBUSY; goto out; } - if (dev->stream_on && !fh->stream_on) { - printk(KERN_INFO "%s device in use by another fh\n", __func__); - rc = -EBUSY; - goto out; - } - - return au0828_set_format(dev, VIDIOC_S_FMT, f); + rc = au0828_set_format(dev, VIDIOC_S_FMT, f); out: + mutex_unlock(&dev->lock); return rc; } @@ -1300,6 +1487,29 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; + format->fmt.vbi.start[0] = 21; + format->fmt.vbi.start[1] = 284; + + return 0; +} + static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { @@ -1345,25 +1555,32 @@ static int vidioc_cropcap(struct file *file, void *priv, static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc = -EINVAL; rc = check_dev(dev); if (rc < 0) return rc; + if (unlikely(type != fh->type)) + return -EINVAL; + + dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { au0828_analog_stream_enable(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); } - mutex_lock(&dev->lock); - rc = res_get(fh); - - if (likely(rc >= 0)) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) rc = videobuf_streamon(&fh->vb_vidq); - mutex_unlock(&dev->lock); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); return rc; } @@ -1371,38 +1588,42 @@ static int vidioc_streamon(struct file *file, void *priv, static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int i; - int ret; - int rc; + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + int i; rc = check_dev(dev); if (rc < 0) return rc; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; if (type != fh->type) return -EINVAL; - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); - ret = au0828_stream_interrupt(dev); - if (ret != 0) - return ret; - } + rc = au0828_stream_interrupt(dev); + if (rc != 0) + return rc; - for (i = 0; i < AU0828_MAX_INPUT; i++) { - if (AUVI_INPUT(i).audio_setup == NULL) - continue; - (AUVI_INPUT(i).audio_setup)(dev, 0); - } + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + (AUVI_INPUT(i).audio_setup)(dev, 0); + } - mutex_lock(&dev->lock); - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - mutex_unlock(&dev->lock); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, AU0828_RESOURCE_VIDEO); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, AU0828_RESOURCE_VBI); + } return 0; } @@ -1527,19 +1748,11 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -#ifdef VBI_IS_WORKING .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, -#endif + .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, -#ifdef VBI_IS_WORKING - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, -#endif .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, @@ -1621,8 +1834,11 @@ int au0828_analog_register(struct au0828_dev *dev, spin_lock_init(&dev->slock); mutex_init(&dev->lock); + /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); dev->width = NTSC_STD_W; dev->height = NTSC_STD_H; @@ -1638,26 +1854,23 @@ int au0828_analog_register(struct au0828_dev *dev, return -ENOMEM; } -#ifdef VBI_IS_WORKING + /* allocate the VBI struct */ dev->vbi_dev = video_device_alloc(); if (NULL == dev->vbi_dev) { dprintk(1, "Can't allocate vbi_device.\n"); kfree(dev->vdev); return -ENOMEM; } -#endif /* Fill the video capture device struct */ *dev->vdev = au0828_video_template; dev->vdev->parent = &dev->usbdev->dev; strcpy(dev->vdev->name, "au0828a video"); -#ifdef VBI_IS_WORKING /* Setup the VBI device */ *dev->vbi_dev = au0828_video_template; dev->vbi_dev->parent = &dev->usbdev->dev; strcpy(dev->vbi_dev->name, "au0828a vbi"); -#endif /* Register the v4l2 device */ video_set_drvdata(dev->vdev, dev); @@ -1669,7 +1882,6 @@ int au0828_analog_register(struct au0828_dev *dev, return -ENODEV; } -#ifdef VBI_IS_WORKING /* Register the vbi device */ video_set_drvdata(dev->vbi_dev, dev); retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1); @@ -1680,7 +1892,6 @@ int au0828_analog_register(struct au0828_dev *dev, video_device_release(dev->vdev); return -ENODEV; } -#endif dprintk(1, "%s completed!\n", __func__); diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h index 207f32dec6a..9905bc4f5f5 100644 --- a/drivers/media/video/au0828/au0828.h +++ b/drivers/media/video/au0828/au0828.h @@ -60,6 +60,10 @@ #define AU0828_MAX_INPUT 4 +/* au0828 resource types (used for res_get/res_lock etc */ +#define AU0828_RESOURCE_VIDEO 0x01 +#define AU0828_RESOURCE_VBI 0x02 + enum au0828_itype { AU0828_VMUX_UNDEFINED = 0, AU0828_VMUX_COMPOSITE, @@ -115,8 +119,10 @@ enum au0828_dev_state { struct au0828_fh { struct au0828_dev *dev; - unsigned int stream_on:1; /* Locks streams */ + unsigned int resources; + struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -145,7 +151,8 @@ struct au0828_usb_isoc_ctl { int tmp_buf_len; /* Stores already requested buffers */ - struct au0828_buffer *buf; + struct au0828_buffer *buf; + struct au0828_buffer *vbi_buf; /* Stores the number of received fields */ int nfields; @@ -194,11 +201,14 @@ struct au0828_dev { /* Analog */ struct v4l2_device v4l2_dev; int users; - unsigned int stream_on:1; /* Locks streams */ + unsigned int resources; /* resources in use */ struct video_device *vdev; struct video_device *vbi_dev; int width; int height; + int vbi_width; + int vbi_height; + u32 vbi_read; u32 field_size; u32 frame_size; u32 bytesperline; @@ -219,6 +229,7 @@ struct au0828_dev { /* Isoc control struct */ struct au0828_dmaqueue vidq; + struct au0828_dmaqueue vbiq; struct au0828_usb_isoc_ctl isoc_ctl; spinlock_t slock; @@ -278,6 +289,9 @@ void au0828_analog_unregister(struct au0828_dev *dev); extern int au0828_dvb_register(struct au0828_dev *dev); extern void au0828_dvb_unregister(struct au0828_dev *dev); +/* au0828-vbi.c */ +extern struct videobuf_queue_ops au0828_vbi_qops; + #define dprintk(level, fmt, arg...)\ do { if (au0828_debug & level)\ printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index 3077c45015f..1a4a89fdf76 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -4,7 +4,7 @@ config VIDEO_BT848 select I2C_ALGOBIT select VIDEO_BTCX select VIDEOBUF_DMA_SG - select VIDEO_IR + depends on VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 407fa61e4cd..685d6597ee7 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -411,7 +411,7 @@ void __devinit init_bttv_i2c_ir(struct bttv *btv) memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list); + i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL); } } diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index c24b1c100e1..0fa9f39f37a 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -583,7 +583,7 @@ bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(btv->c.pci,&buf->bottom); btcx_riscmem_free(btv->c.pci,&buf->top); diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c index ef1f8939998..58d193ff591 100644 --- a/drivers/media/video/cpia_usb.c +++ b/drivers/media/video/cpia_usb.c @@ -584,7 +584,6 @@ static void cpia_disconnect(struct usb_interface *intf) { struct cam_data *cam = usb_get_intfdata(intf); struct usb_cpia *ucpia; - struct usb_device *udev; usb_set_intfdata(intf, NULL); if (!cam) @@ -606,8 +605,6 @@ static void cpia_disconnect(struct usb_interface *intf) if (waitqueue_active(&ucpia->wq_stream)) wake_up_interruptible(&ucpia->wq_stream); - udev = interface_to_usbdev(intf); - ucpia->curbuff = ucpia->workbuff = NULL; vfree(ucpia->buffers[2]); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 3cc135a98d8..cc9e84d75ea 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -26,10 +26,10 @@ #include <linux/ioctl.h> #include <asm/uaccess.h> #include <linux/i2c.h> -#include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); @@ -43,6 +43,21 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); +struct cs53l32a_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; +}; + +static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cs53l32a_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; +} + /* ----------------------------------------------------------------------- */ static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) @@ -74,31 +89,20 @@ static int cs53l32a_s_routing(struct v4l2_subdev *sd, return 0; } -static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl) { - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0; - return 0; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = (s8)cs53l32a_read(sd, 0x04); - return 0; -} + struct v4l2_subdev *sd = to_sd(ctrl); -static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30); + return 0; + case V4L2_CID_AUDIO_VOLUME: + cs53l32a_write(sd, 0x04, (u8)ctrl->val); + cs53l32a_write(sd, 0x05, (u8)ctrl->val); return 0; } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 12 || ctrl->value < -96) - return -EINVAL; - cs53l32a_write(sd, 0x04, (u8) ctrl->value); - cs53l32a_write(sd, 0x05, (u8) ctrl->value); - return 0; + return -EINVAL; } static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) @@ -111,23 +115,30 @@ static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_id static int cs53l32a_log_status(struct v4l2_subdev *sd) { + struct cs53l32a_state *state = to_state(sd); u8 v = cs53l32a_read(sd, 0x01); - u8 m = cs53l32a_read(sd, 0x03); - s8 vol = cs53l32a_read(sd, 0x04); - v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3, - (m & 0xC0) ? " (muted)" : ""); - v4l2_info(sd, "Volume: %d dB\n", vol); + v4l2_info(sd, "Input: %d\n", (v >> 4) & 3); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); return 0; } /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { + .s_ctrl = cs53l32a_s_ctrl, +}; + static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { .log_status = cs53l32a_log_status, .g_chip_ident = cs53l32a_g_chip_ident, - .g_ctrl = cs53l32a_g_ctrl, - .s_ctrl = cs53l32a_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { @@ -151,6 +162,7 @@ static const struct v4l2_subdev_ops cs53l32a_ops = { static int cs53l32a_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct cs53l32a_state *state; struct v4l2_subdev *sd; int i; @@ -164,9 +176,10 @@ static int cs53l32a_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) + state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL); + if (state == NULL) return -ENOMEM; + sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); for (i = 1; i <= 7; i++) { @@ -175,15 +188,29 @@ static int cs53l32a_probe(struct i2c_client *client, v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } + v4l2_ctrl_handler_init(&state->hdl, 2); + v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ - cs53l32a_write(sd, 0x01, (u8) 0x21); - cs53l32a_write(sd, 0x02, (u8) 0x29); - cs53l32a_write(sd, 0x03, (u8) 0x30); - cs53l32a_write(sd, 0x04, (u8) 0x00); - cs53l32a_write(sd, 0x05, (u8) 0x00); - cs53l32a_write(sd, 0x06, (u8) 0x00); - cs53l32a_write(sd, 0x07, (u8) 0x00); + cs53l32a_write(sd, 0x01, 0x21); + cs53l32a_write(sd, 0x02, 0x29); + cs53l32a_write(sd, 0x03, 0x30); + cs53l32a_write(sd, 0x04, 0x00); + cs53l32a_write(sd, 0x05, 0x00); + cs53l32a_write(sd, 0x06, 0x00); + cs53l32a_write(sd, 0x07, 0x00); /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ @@ -198,9 +225,11 @@ static int cs53l32a_probe(struct i2c_client *client, static int cs53l32a_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cs53l32a_state *state = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index baf7e91ee0f..76c054d1eef 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -3,7 +3,7 @@ config VIDEO_CX18 depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL depends on INPUT # due to VIDEO_IR select I2C_ALGOBIT - select VIDEO_IR + depends on VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 809f7d37129..73ce90c2f57 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -117,7 +117,8 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, break; } - return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; + return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? + -1 : 0; } int cx18_i2c_register(struct cx18 *cx, unsigned idx) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 20eaf38ba95..d6792405f8d 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1081,7 +1081,7 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); - struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; + struct cx18_open_id *id = filp->private_data; struct cx18 *cx = id->cx; long res; diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig index 477d4ab5e9a..5ac7eceecec 100644 --- a/drivers/media/video/cx231xx/Kconfig +++ b/drivers/media/video/cx231xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_CX231XX depends on VIDEO_DEV && I2C && INPUT select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_IR + depends on VIDEO_IR select VIDEOBUF_VMALLOC select VIDEO_CX25840 diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 2bf44ef10fe..e5c3c8da4be 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -38,6 +38,145 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); +/********************** COMMON CODE *********************/ + +/* definitions for audio properties bits 29-28 */ +#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 +#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 +#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 + +static const char *cx2341x_get_name(u32 id) +{ + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return "Spatial Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + return "Spatial Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return "Spatial Luma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return "Spatial Chroma Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return "Temporal Filter Mode"; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + return "Temporal Filter"; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return "Median Filter Type"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + return "Median Luma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + return "Median Luma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + return "Median Chroma Filter Maximum"; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + return "Median Chroma Filter Minimum"; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return "Insert Navigation Packets"; + } + return NULL; +} + +static const char **cx2341x_get_menu(u32 id) +{ + static const char *cx2341x_video_spatial_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + "1D Vertical", + "2D H/V Separable", + "2D Symmetric non-separable", + NULL + }; + + static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { + "Off", + "1D Horizontal", + NULL + }; + + static const char *cx2341x_video_temporal_filter_mode_menu[] = { + "Manual", + "Auto", + NULL + }; + + static const char *cx2341x_video_median_filter_type_menu[] = { + "Off", + "Horizontal", + "Vertical", + "Horizontal/Vertical", + "Diagonal", + NULL + }; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + return cx2341x_video_spatial_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_luma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + return cx2341x_video_chroma_spatial_filter_type_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + return cx2341x_video_temporal_filter_mode_menu; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + return cx2341x_video_median_filter_type_menu; + } + return NULL; +} + +static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) +{ + *name = cx2341x_get_name(id); + *flags = 0; + + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *type = V4L2_CTRL_TYPE_MENU; + *min = 0; + *step = 0; + break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + *type = V4L2_CTRL_TYPE_BOOLEAN; + *min = 0; + *max = *step = 1; + break; + default: + *type = V4L2_CTRL_TYPE_INTEGER; + break; + } + switch (id) { + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + *flags |= V4L2_CTRL_FLAG_UPDATE; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + *flags |= V4L2_CTRL_FLAG_SLIDER; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; + } +} + + +/********************** OLD CODE *********************/ + /* Must be sorted from low to high control ID! */ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_CLASS, @@ -134,8 +273,6 @@ static const struct cx2341x_mpeg_params default_params = { .video_chroma_median_filter_top = 255, .video_chroma_median_filter_bottom = 0, }; - - /* Map the control ID to the correct field in the cx2341x_mpeg_params struct. Return -EINVAL if the ID is unknown, else return 0. */ static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, @@ -415,83 +552,33 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, { const char *name; - qctrl->flags = 0; switch (qctrl->id) { /* MPEG controls */ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - name = "Spatial Filter Mode"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - name = "Spatial Filter"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - name = "Spatial Luma Filter Type"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - name = "Spatial Chroma Filter Type"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - name = "Temporal Filter Mode"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - name = "Temporal Filter"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - name = "Median Filter Type"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - name = "Median Luma Filter Maximum"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - name = "Median Luma Filter Minimum"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - name = "Median Chroma Filter Maximum"; - break; case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - name = "Median Chroma Filter Minimum"; - break; case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - name = "Insert Navigation Packets"; - break; + cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type, + &min, &max, &step, &def, &qctrl->flags); + qctrl->minimum = min; + qctrl->maximum = max; + qctrl->step = step; + qctrl->default_value = def; + qctrl->reserved[0] = qctrl->reserved[1] = 0; + strlcpy(qctrl->name, name, sizeof(qctrl->name)); + return 0; default: return v4l2_ctrl_query_fill(qctrl, min, max, step, def); } - switch (qctrl->id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - qctrl->type = V4L2_CTRL_TYPE_MENU; - min = 0; - step = 1; - break; - case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; - min = 0; - max = 1; - step = 1; - break; - default: - qctrl->type = V4L2_CTRL_TYPE_INTEGER; - break; - } - switch (qctrl->id) { - case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - qctrl->flags |= V4L2_CTRL_FLAG_UPDATE; - break; - } - qctrl->minimum = min; - qctrl->maximum = max; - qctrl->step = step; - qctrl->default_value = def; - qctrl->reserved[0] = qctrl->reserved[1] = 0; - snprintf(qctrl->name, sizeof(qctrl->name), name); - return 0; } int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, @@ -797,42 +884,6 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) NULL }; - static const char *cx2341x_video_spatial_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_luma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - "1D Vertical", - "2D H/V Separable", - "2D Symmetric non-separable", - NULL - }; - - static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = { - "Off", - "1D Horizontal", - NULL - }; - - static const char *cx2341x_video_temporal_filter_mode_menu[] = { - "Manual", - "Auto", - NULL - }; - - static const char *cx2341x_video_median_filter_type_menu[] = { - "Off", - "Horizontal", - "Vertical", - "Horizontal/Vertical", - "Diagonal", - NULL - }; - switch (id) { case V4L2_CID_MPEG_STREAM_TYPE: return (p->capabilities & CX2341X_CAP_HAS_TS) ? @@ -844,26 +895,17 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return NULL; case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: - return cx2341x_video_spatial_filter_mode_menu; case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_luma_spatial_filter_type_menu; case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: - return cx2341x_video_chroma_spatial_filter_type_menu; case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: - return cx2341x_video_temporal_filter_mode_menu; case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: - return cx2341x_video_median_filter_type_menu; + return cx2341x_get_menu(id); default: return v4l2_ctrl_get_menu(id); } } EXPORT_SYMBOL(cx2341x_ctrl_get_menu); -/* definitions for audio properties bits 29-28 */ -#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 -#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 -#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 - static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) { params->audio_properties = @@ -1195,9 +1237,490 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) } EXPORT_SYMBOL(cx2341x_log_status); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ + +/********************** NEW CODE *********************/ + +static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct cx2341x_handler, hdl); +} + +static int cx2341x_hdl_api(struct cx2341x_handler *hdl, + u32 cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i; + + va_start(vargs, args); + + for (i = 0; i < args; i++) + data[i] = va_arg(vargs, int); + va_end(vargs); + return hdl->func(hdl->priv, cmd, args, 0, data); +} + +/* ctrl->handler->lock is held, so it is safe to access cur.val */ +static inline int cx2341x_neq(struct v4l2_ctrl *ctrl) +{ + return ctrl && ctrl->val != ctrl->cur.val; +} + +static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_B_FRAMES: { + /* video gop cluster */ + int b = val + 1; + int gop = hdl->video_gop_size->val; + + gop = b * ((gop + b - 1) / b); + + /* Max GOP size = 34 */ + while (gop > 34) + gop -= b; + hdl->video_gop_size->val = gop; + break; + } + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + hdl->video_encoding->val = + (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS || + hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ? + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : + V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + /* MPEG-1 implies CBR */ + hdl->video_bitrate_mode->val = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + /* peak bitrate shall be >= normal bitrate */ + if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + hdl->video_bitrate_peak->val < hdl->video_bitrate->val) + hdl->video_bitrate_peak->val = hdl->video_bitrate->val; + break; + } + return 0; +} + +static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const int mpeg_stream_type[] = { + 0, /* MPEG-2 PS */ + 1, /* MPEG-2 TS */ + 2, /* MPEG-1 SS */ + 14, /* DVD */ + 11, /* VCD */ + 12, /* SVCD */ + }; + struct cx2341x_handler *hdl = to_cxhdl(ctrl); + s32 val = ctrl->val; + u32 props; + int err; + + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (hdl->ops && hdl->ops->s_stream_vbi_fmt) + return hdl->ops->s_stream_vbi_fmt(hdl, val); + return 0; + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val); + + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val); + + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + /* audio properties cluster */ + props = (hdl->audio_sampling_freq->val << 0) | + (hdl->audio_mode->val << 8) | + (hdl->audio_mode_extension->val << 10) | + (hdl->audio_crc->val << 14); + if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) + props |= 3 << 12; + else + props |= hdl->audio_emphasis->val << 12; + + if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) { + props |= +#if 1 + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | +#endif + (hdl->audio_ac3_bitrate->val << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + props |= + ((3 - hdl->audio_encoding->val) << 2) | + ((1 + hdl->audio_l2_bitrate->val) << 4); + } + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props); + if (err) + return err; + + hdl->audio_properties = props; + if (hdl->audio_ac3_bitrate) { + int is_ac3 = hdl->audio_encoding->val == + V4L2_MPEG_AUDIO_ENCODING_AC3; + + v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3); + v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3); + } + v4l2_ctrl_activate(hdl->audio_mode_extension, + hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO); + if (cx2341x_neq(hdl->audio_sampling_freq) && + hdl->ops && hdl->ops->s_audio_sampling_freq) + return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val); + if (cx2341x_neq(hdl->audio_mode) && + hdl->ops && hdl->ops->s_audio_mode) + return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + /* video gop cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2, + hdl->video_gop_size->val, + hdl->video_b_frames->val + 1); + + case V4L2_CID_MPEG_STREAM_TYPE: + /* stream type cluster */ + err = cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]); + if (err) + return err; + + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5, + hdl->video_bitrate_mode->val, + hdl->video_bitrate->val, + hdl->video_bitrate_peak->val / 400, 0, 0); + if (err) + return err; + + v4l2_ctrl_activate(hdl->video_bitrate_mode, + hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1); + v4l2_ctrl_activate(hdl->video_bitrate_peak, + hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + if (cx2341x_neq(hdl->video_encoding) && + hdl->ops && hdl->ops->s_video_encoding) + return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val); + return 0; + + case V4L2_CID_MPEG_VIDEO_MUTE: + /* video mute cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1, + hdl->video_mute->val | + (hdl->video_mute_yuv->val << 8)); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: { + int active_filter; + + /* video filter mode */ + err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, + hdl->video_spatial_filter_mode->val | + (hdl->video_temporal_filter_mode->val << 1), + hdl->video_median_filter_type->val); + if (err) + return err; + + active_filter = hdl->video_spatial_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter); + v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter); + active_filter = hdl->video_temporal_filter_mode->val != + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO; + v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter); + active_filter = hdl->video_median_filter_type->val != + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF; + v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter); + v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter); + return 0; + } + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + /* video filter type cluster */ + return cx2341x_hdl_api(hdl, + CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, + hdl->video_luma_spatial_filter_type->val, + hdl->video_chroma_spatial_filter_type->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + /* video filter cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, + hdl->video_spatial_filter->val, + hdl->video_temporal_filter->val); + + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + /* video median cluster */ + return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4, + hdl->video_luma_median_filter_bottom->val, + hdl->video_luma_median_filter_top->val, + hdl->video_chroma_median_filter_bottom->val, + hdl->video_chroma_median_filter_top->val); + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops cx2341x_ops = { + .try_ctrl = cx2341x_try_ctrl, + .s_ctrl = cx2341x_s_ctrl, +}; + +static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + struct v4l2_ctrl_config cfg; + + cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags); + cfg.ops = &cx2341x_ops; + cfg.id = id; + cfg.min = min; + cfg.max = max; + cfg.def = def; + if (cfg.type == V4L2_CTRL_TYPE_MENU) { + cfg.step = 0; + cfg.menu_skip_mask = step; + cfg.qmenu = cx2341x_get_menu(id); + } else { + cfg.step = step; + cfg.menu_skip_mask = 0; + } + return v4l2_ctrl_new_custom(hdl, &cfg, NULL); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl, + u32 id, s32 min, s32 max, s32 step, s32 def) +{ + return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def); +} + +static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl, + u32 id, s32 max, s32 mask, s32 def) +{ + return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def); +} + +int cx2341x_handler_init(struct cx2341x_handler *cxhdl, + unsigned nr_of_controls_hint) +{ + struct v4l2_ctrl_handler *hdl = &cxhdl->hdl; + u32 caps = cxhdl->capabilities; + int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI; + int has_ac3 = caps & CX2341X_CAP_HAS_AC3; + int has_ts = caps & CX2341X_CAP_HAS_TS; + + cxhdl->width = 720; + cxhdl->height = 480; + + v4l2_ctrl_handler_init(hdl, nr_of_controls_hint); + + /* Add controls in ascending control ID order for fastest + insertion time. */ + cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_STREAM_VBI_FMT, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff, + V4L2_MPEG_AUDIO_L2_BITRATE_224K); + cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE, + V4L2_MPEG_AUDIO_MODE_MONO, 0, + V4L2_MPEG_AUDIO_MODE_STEREO); + cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_EMPHASIS, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_CRC, + V4L2_MPEG_AUDIO_CRC_CRC16, 0, + V4L2_MPEG_AUDIO_CRC_NONE); + + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0); + if (has_ac3) + cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K); + cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_221x100, 0, + V4L2_MPEG_VIDEO_ASPECT_4x3); + cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2); + cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 34, 1, cxhdl->is_50hz ? 12 : 15); + cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); + cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, 27000000, 1, 6000000); + cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, 27000000, 1, 8000000); + cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0); + cxhdl->video_mute = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0); + cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080); + + /* CX23415/6 specific */ + cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); + cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, + 0, 15, 1, 0); + cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, + 0, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + 0, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR); + cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, + 0, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); + cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, + 0, 31, 1, 8); + cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, + 0, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); + cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, + 0, 255, 1, 0); + cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl, + V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + 0, 255, 1, 255); + cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, + 0, 1, 1, 0); + + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + + v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq); + v4l2_ctrl_cluster(2, &cxhdl->video_b_frames); + v4l2_ctrl_cluster(5, &cxhdl->stream_type); + v4l2_ctrl_cluster(2, &cxhdl->video_mute); + v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode); + v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type); + v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter); + v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top); + + return 0; +} +EXPORT_SYMBOL(cx2341x_handler_init); + +void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz) +{ + cxhdl->is_50hz = is_50hz; + cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15; +} +EXPORT_SYMBOL(cx2341x_handler_set_50hz); + +int cx2341x_handler_setup(struct cx2341x_handler *cxhdl) +{ + int h = cxhdl->height; + int w = cxhdl->width; + int err; + + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0); + if (err) + return err; + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz); + if (err) + return err; + + if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) { + w /= 2; + h /= 2; + } + err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w); + if (err) + return err; + return v4l2_ctrl_handler_setup(&cxhdl->hdl); +} +EXPORT_SYMBOL(cx2341x_handler_setup); + +void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy) +{ + v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy); + v4l2_ctrl_grab(cxhdl->audio_encoding, busy); + v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy); + v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy); + v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy); + v4l2_ctrl_grab(cxhdl->stream_type, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate, busy); + v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy); +} +EXPORT_SYMBOL(cx2341x_handler_set_busy); diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index bcdda9a9aa9..e1367b35647 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -5,7 +5,7 @@ config VIDEO_CX23885 select VIDEO_BTCX select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_IR + depends on IR_CORE select VIDEOBUF_DVB select VIDEOBUF_DMA_SG select VIDEO_CX25840 diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 5787ae24363..e2ee95f660d 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,7 +1,8 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ - cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \ - netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o + cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \ + cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \ + cx23885-f300.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cx23885-av.c b/drivers/media/video/cx23885/cx23885-av.c new file mode 100644 index 00000000000..134ebddd860 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-av.c @@ -0,0 +1,35 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * AV device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "cx23885.h" + +void cx23885_av_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, cx25840_work); + bool handled; + + v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine, + PCI_MSK_AV_CORE, &handled); + cx23885_irq_enable(dev, PCI_MSK_AV_CORE); +} diff --git a/drivers/media/video/cx23885/cx23885-av.h b/drivers/media/video/cx23885/cx23885-av.h new file mode 100644 index 00000000000..d2915c3e53a --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-av.h @@ -0,0 +1,27 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * AV device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_AV_H_ +#define _CX23885_AV_H_ +void cx23885_av_work_handler(struct work_struct *work); +#endif diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index d639186f645..e76ce8709af 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -30,6 +30,16 @@ #include "netup-init.h" #include "cx23888-ir.h" +static unsigned int enable_885_ir; +module_param(enable_885_ir, int, 0644); +MODULE_PARM_DESC(enable_885_ir, + "Enable integrated IR controller for supported\n" + "\t\t CX2388[57] boards that are wired for it:\n" + "\t\t\tHVR-1250 (reported safe)\n" + "\t\t\tTeVii S470 (reported unsafe)\n" + "\t\t This can cause an interrupt storm with some cards.\n" + "\t\t Default: 0 [Disabled]"); + /* ------------------------------------------------------------------ */ /* board config info */ @@ -408,10 +418,18 @@ struct cx23885_subid cx23885_subids[] = { .card = CX23885_BOARD_HAUPPAUGE_HVR1275, }, { .subvendor = 0x0070, + .subdevice = 0x221d, + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, + }, { + .subvendor = 0x0070, .subdevice = 0x2251, .card = CX23885_BOARD_HAUPPAUGE_HVR1255, }, { .subvendor = 0x0070, + .subdevice = 0x2259, + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, + }, { + .subvendor = 0x0070, .subdevice = 0x2291, .card = CX23885_BOARD_HAUPPAUGE_HVR1210, }, { @@ -419,6 +437,38 @@ struct cx23885_subid cx23885_subids[] = { .subdevice = 0x2295, .card = CX23885_BOARD_HAUPPAUGE_HVR1210, }, { + .subvendor = 0x0070, + .subdevice = 0x2299, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x229d, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x22f0, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f1, + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f2, + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f3, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x22f4, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f5, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ + }, { .subvendor = 0x14f1, .subdevice = 0x8651, .card = CX23885_BOARD_MYGICA_X8506, @@ -586,6 +636,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 79101: /* WinTV-HVR1250 (PCIe, Retail, IR, half height, ATSC and Basic analog */ + case 79501: + /* WinTV-HVR1250 (PCIe, No IR, half height, + ATSC [at least] and Basic analog) */ case 79561: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ @@ -919,9 +972,37 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) int cx23885_ir_init(struct cx23885_dev *dev) { + static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = { + { + .flags = V4L2_SUBDEV_IO_PIN_INPUT, + .pin = CX23885_PIN_IR_RX_GPIO19, + .function = CX23885_PAD_IR_RX, + .value = 0, + .strength = CX25840_PIN_DRIVE_MEDIUM, + }, { + .flags = V4L2_SUBDEV_IO_PIN_OUTPUT, + .pin = CX23885_PIN_IR_TX_GPIO20, + .function = CX23885_PAD_IR_TX, + .value = 0, + .strength = CX25840_PIN_DRIVE_MEDIUM, + } + }; + const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg); + + static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = { + { + .flags = V4L2_SUBDEV_IO_PIN_INPUT, + .pin = CX23885_PIN_IR_RX_GPIO19, + .function = CX23885_PAD_IR_RX, + .value = 0, + .strength = CX25840_PIN_DRIVE_MEDIUM, + } + }; + const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg); + + struct v4l2_subdev_ir_parameters params; int ret = 0; switch (dev->board) { - case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1800: @@ -939,7 +1020,41 @@ int cx23885_ir_init(struct cx23885_dev *dev) if (ret) break; dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR); - dev->pci_irqmask |= PCI_MSK_IR; + v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, + ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg); + /* + * For these boards we need to invert the Tx output via the + * IR controller to have the LED off while idle + */ + v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, ¶ms); + params.enable = false; + params.shutdown = false; + params.invert_level = true; + v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms); + params.shutdown = true; + v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms); + break; + case CX23885_BOARD_TEVII_S470: + if (!enable_885_ir) + break; + dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); + if (dev->sd_ir == NULL) { + ret = -ENODEV; + break; + } + v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, + ir_rx_pin_cfg_count, ir_rx_pin_cfg); + break; + case CX23885_BOARD_HAUPPAUGE_HVR1250: + if (!enable_885_ir) + break; + dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); + if (dev->sd_ir == NULL) { + ret = -ENODEV; + break; + } + v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, + ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg); break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: request_module("ir-kbd-i2c"); @@ -954,11 +1069,16 @@ void cx23885_ir_fini(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: - dev->pci_irqmask &= ~PCI_MSK_IR; - cx_clear(PCI_INT_MSK, PCI_MSK_IR); + cx23885_irq_remove(dev, PCI_MSK_IR); cx23888_ir_remove(dev); dev->sd_ir = NULL; break; + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: + cx23885_irq_remove(dev, PCI_MSK_AV_CORE); + /* sd_ir is a duplicate pointer to the AV Core, just clear it */ + dev->sd_ir = NULL; + break; } } @@ -967,8 +1087,13 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: - if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR)) - cx_set(PCI_INT_MSK, PCI_MSK_IR); + if (dev->sd_ir) + cx23885_irq_add_enable(dev, PCI_MSK_IR); + break; + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: + if (dev->sd_ir) + cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE); break; } } @@ -988,6 +1113,13 @@ void cx23885_card_setup(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1250: + if (dev->i2c_bus[0].i2c_rc == 0) { + if (eeprom[0x80] != 0x84) + hauppauge_eeprom(dev, eeprom+0xc0); + else + hauppauge_eeprom(dev, eeprom+0x80); + } + break; case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1400: @@ -1096,6 +1228,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) * loaded, ensure this happens. */ switch (dev->board) { + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: + /* Currently only enabled for the integrated IR controller */ + if (!enable_885_ir) + break; case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: @@ -1111,7 +1248,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", "cx25840", 0x88 >> 1, NULL); - v4l2_subdev_call(dev->sd_cx25840, core, load_fw); + if (dev->sd_cx25840) { + dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE; + v4l2_subdev_call(dev->sd_cx25840, core, load_fw); + } break; } diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 0dde57e96d3..f6b62e7398a 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -34,6 +34,7 @@ #include "cimax2.h" #include "cx23888-ir.h" #include "cx23885-ir.h" +#include "cx23885-av.h" #include "cx23885-input.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); @@ -299,6 +300,83 @@ static struct sram_channel cx23887_sram_channels[] = { }, }; +void cx23885_irq_add(struct cx23885_dev *dev, u32 mask) +{ + unsigned long flags; + spin_lock_irqsave(&dev->pci_irqmask_lock, flags); + + dev->pci_irqmask |= mask; + + spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); +} + +void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask) +{ + unsigned long flags; + spin_lock_irqsave(&dev->pci_irqmask_lock, flags); + + dev->pci_irqmask |= mask; + cx_set(PCI_INT_MSK, mask); + + spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); +} + +void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask) +{ + u32 v; + unsigned long flags; + spin_lock_irqsave(&dev->pci_irqmask_lock, flags); + + v = mask & dev->pci_irqmask; + if (v) + cx_set(PCI_INT_MSK, v); + + spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); +} + +static inline void cx23885_irq_enable_all(struct cx23885_dev *dev) +{ + cx23885_irq_enable(dev, 0xffffffff); +} + +void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask) +{ + unsigned long flags; + spin_lock_irqsave(&dev->pci_irqmask_lock, flags); + + cx_clear(PCI_INT_MSK, mask); + + spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); +} + +static inline void cx23885_irq_disable_all(struct cx23885_dev *dev) +{ + cx23885_irq_disable(dev, 0xffffffff); +} + +void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask) +{ + unsigned long flags; + spin_lock_irqsave(&dev->pci_irqmask_lock, flags); + + dev->pci_irqmask &= ~mask; + cx_clear(PCI_INT_MSK, mask); + + spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); +} + +static u32 cx23885_irq_get_mask(struct cx23885_dev *dev) +{ + u32 v; + unsigned long flags; + spin_lock_irqsave(&dev->pci_irqmask_lock, flags); + + v = cx_read(PCI_INT_MSK); + + spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); + return v; +} + static int cx23885_risc_decode(u32 risc) { static char *instr[16] = { @@ -548,7 +626,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev) cx_write(UART_CTL, 0); /* Disable Interrupts */ - cx_write(PCI_INT_MSK, 0); + cx23885_irq_disable_all(dev); cx_write(VID_A_INT_MSK, 0); cx_write(VID_B_INT_MSK, 0); cx_write(VID_C_INT_MSK, 0); @@ -774,6 +852,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) { int i; + spin_lock_init(&dev->pci_irqmask_lock); + mutex_init(&dev->lock); mutex_init(&dev->gpio_lock); @@ -820,9 +900,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->pci_bus = dev->pci->bus->number; dev->pci_slot = PCI_SLOT(dev->pci->devfn); - dev->pci_irqmask = 0x001f00; + cx23885_irq_add(dev, 0x001f00); if (cx23885_boards[dev->board].cimax > 0) - dev->pci_irqmask |= 0x01800000; /* for CiMaxes */ + cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */ /* External Master 1 Bus */ dev->i2c_bus[0].nr = 0; @@ -1142,7 +1222,7 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb, 0, 0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; @@ -1156,7 +1236,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__, cx_read(DEV_CNTRL2)); dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__, - cx_read(PCI_INT_MSK)); + cx23885_irq_get_mask(dev)); dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__, cx_read(AUDIO_INT_INT_MSK)); dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__, @@ -1292,7 +1372,8 @@ static int cx23885_start_dma(struct cx23885_tsport *port, dprintk(1, "%s() enabling TS int's and DMA\n", __func__); cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); cx_set(port->reg_dma_ctl, port->dma_ctl_val); - cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask); + cx23885_irq_add(dev, port->pci_irqmask); + cx23885_irq_enable_all(dev); break; default: BUG(); @@ -1650,10 +1731,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) u32 ts1_status, ts1_mask; u32 ts2_status, ts2_mask; int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; - bool ir_handled = false; + bool subdev_handled; pci_status = cx_read(PCI_INT_STAT); - pci_mask = cx_read(PCI_INT_MSK); + pci_mask = cx23885_irq_get_mask(dev); vida_status = cx_read(VID_A_INT_STAT); vida_mask = cx_read(VID_A_INT_MSK); ts1_status = cx_read(VID_B_INT_STAT); @@ -1681,7 +1762,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A | PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT | PCI_MSK_GPIO0 | PCI_MSK_GPIO1 | - PCI_MSK_IR)) { + PCI_MSK_AV_CORE | PCI_MSK_IR)) { if (pci_status & PCI_MSK_RISC_RD) dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", @@ -1731,6 +1812,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", PCI_MSK_GPIO1); + if (pci_status & PCI_MSK_AV_CORE) + dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n", + PCI_MSK_AV_CORE); + if (pci_status & PCI_MSK_IR) dprintk(7, " (PCI_MSK_IR 0x%08x)\n", PCI_MSK_IR); @@ -1765,12 +1850,22 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) handled += cx23885_video_irq(dev, vida_status); if (pci_status & PCI_MSK_IR) { - v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine, - pci_status, &ir_handled); - if (ir_handled) + subdev_handled = false; + v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine, + pci_status, &subdev_handled); + if (subdev_handled) handled++; } + if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) { + cx23885_irq_disable(dev, PCI_MSK_AV_CORE); + if (!schedule_work(&dev->cx25840_work)) + printk(KERN_ERR "%s: failed to set up deferred work for" + " AV Core/IR interrupt. Interrupt is disabled" + " and won't be re-enabled\n", dev->name); + handled++; + } + if (handled) cx_write(PCI_INT_STAT, pci_status); out: @@ -1788,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd, dev = to_cx23885(sd->v4l2_dev); switch (notification) { - case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */ + case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */ if (sd == dev->sd_ir) cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg); break; - case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */ + case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */ if (sd == dev->sd_ir) cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg); break; @@ -1801,6 +1896,7 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd, static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev) { + INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler); INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler); INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler); dev->v4l2_dev.notify = cx23885_v4l2_dev_notify; @@ -1953,8 +2049,12 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, goto fail_irq; } - err = request_irq(pci_dev->irq, cx23885_irq, - IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (!pci_enable_msi(pci_dev)) + err = request_irq(pci_dev->irq, cx23885_irq, + IRQF_DISABLED, dev->name, dev); + else + err = request_irq(pci_dev->irq, cx23885_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); if (err < 0) { printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, pci_dev->irq); @@ -1963,7 +2063,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, switch (dev->board) { case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: - cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */ break; } @@ -2000,6 +2100,7 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) /* unregister stuff */ free_irq(pci_dev->irq, dev); + pci_disable_msi(pci_dev); cx23885_dev_unregister(dev); v4l2_device_unregister(v4l2_dev); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 0a199d774d9..3d70af28388 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -991,7 +991,7 @@ static int dvb_register(struct cx23885_tsport *port) ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, &dev->pci->dev, adapter_nr, 0, cx23885_dvb_fe_ioctl_override); - if (!ret) + if (ret) return ret; /* init CI & MAC */ diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index d4746e06451..ed3d8f55029 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -99,7 +99,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, if (!i2c_wait_done(i2c_adap)) return -EIO; if (!i2c_slave_did_ack(i2c_adap)) - return -EIO; + return -ENXIO; dprintk(1, "%s() returns 0\n", __func__); return 0; @@ -120,11 +120,12 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, cx_write(bus->reg_wdata, wdata); cx_write(bus->reg_ctrl, ctrl); - retval = i2c_wait_done(i2c_adap); - if (retval < 0) - goto err; - if (retval == 0) + if (!i2c_wait_done(i2c_adap)) goto eio; + if (!i2c_slave_did_ack(i2c_adap)) { + retval = -ENXIO; + goto err; + } if (i2c_debug) { printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]); if (!(ctrl & I2C_NOSTOP)) @@ -145,10 +146,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, cx_write(bus->reg_wdata, wdata); cx_write(bus->reg_ctrl, ctrl); - retval = i2c_wait_done(i2c_adap); - if (retval < 0) - goto err; - if (retval == 0) + if (!i2c_wait_done(i2c_adap)) goto eio; if (i2c_debug) { dprintk(1, " %02x", msg->buf[cnt]); @@ -185,7 +183,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, if (!i2c_wait_done(i2c_adap)) return -EIO; if (!i2c_slave_did_ack(i2c_adap)) - return -EIO; + return -ENXIO; dprintk(1, "%s() returns 0\n", __func__); @@ -209,11 +207,12 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, cx_write(bus->reg_addr, msg->addr << 25); cx_write(bus->reg_ctrl, ctrl); - retval = i2c_wait_done(i2c_adap); - if (retval < 0) - goto err; - if (retval == 0) + if (!i2c_wait_done(i2c_adap)) goto eio; + if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) { + retval = -ENXIO; + goto err; + } msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff; if (i2c_debug) { dprintk(1, " %02x", msg->buf[cnt]); @@ -365,17 +364,10 @@ int cx23885_i2c_register(struct cx23885_i2c *bus) memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - /* - * We can't call i2c_new_probed_device() because it uses - * quick writes for probing and the IR receiver device only - * replies to reads. - */ - if (i2c_smbus_xfer(&bus->i2c_adap, addr_list[0], 0, - I2C_SMBUS_READ, 0, I2C_SMBUS_QUICK, - NULL) >= 0) { - info.addr = addr_list[0]; - i2c_new_device(&bus->i2c_adap, &info); - } + /* Use quick read command for probe, some IR chips don't + * support writes */ + i2c_new_probed_device(&bus->i2c_adap, &info, addr_list, + i2c_probe_func_quick_read); } return bus->i2c_rc; diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index 5de6ba98f7a..bb61870b8d6 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -37,161 +37,41 @@ #include <linux/input.h> #include <linux/slab.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include <media/v4l2-subdev.h> #include "cx23885.h" -#define RC5_BITS 14 -#define RC5_HALF_BITS (2*RC5_BITS) -#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1) - -#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */ -#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */ - -#define RC5_EXTENDED_COMMAND_OFFSET 64 - #define MODULE_NAME "cx23885" -static inline unsigned int rc5_command(u32 rc5_baseband) -{ - return RC5_INSTR(rc5_baseband) + - ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED) - ? RC5_EXTENDED_COMMAND_OFFSET : 0); -} - -static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev) -{ - struct card_ir *ir_input = dev->ir_input; - unsigned int code, command; - u32 rc5; - - /* Ignore codes that are too short to be valid RC-5 */ - if (ir_input->last_bit < (RC5_HALF_BITS - 1)) - return; - - /* The library has the manchester coding backwards; XOR to adapt. */ - code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK; - rc5 = ir_rc5_decode(code); - - switch (RC5_START(rc5)) { - case RC5_START_BITS_NORMAL: - break; - case RC5_START_BITS_EXTENDED: - /* Don't allow if the remote only emits standard commands */ - if (ir_input->start == RC5_START_BITS_NORMAL) - return; - break; - default: - return; - } - - if (ir_input->addr != RC5_ADDR(rc5)) - return; - - /* Don't generate a keypress for RC-5 auto-repeated keypresses */ - command = rc5_command(rc5); - if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) || - command != rc5_command(ir_input->last_rc5) || - /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */ - RC5_START(ir_input->last_rc5) == 0) { - /* This keypress is differnet: not an auto repeat */ - ir_input_nokey(ir_input->dev, &ir_input->ir); - ir_input_keydown(ir_input->dev, &ir_input->ir, command); - } - ir_input->last_rc5 = rc5; - - /* Schedule when we should do the key up event: ir_input_nokey() */ - mod_timer(&ir_input->timer_keyup, - jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout)); -} - -static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev, - u32 ns_pulse) -{ - const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */ - struct card_ir *ir_input = dev->ir_input; - int i, level, quarterbits, halfbits; - - if (!ir_input->active) { - ir_input->active = 1; - /* assume an initial space that we may not detect or measure */ - ir_input->code = 0; - ir_input->last_bit = 0; - } - - if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { - ir_input->last_bit++; /* Account for the final space */ - ir_input->active = 0; - cx23885_input_process_raw_rc5(dev); - return; - } - - level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0; - - /* Skip any leading space to sync to the start bit */ - if (ir_input->last_bit == 0 && level == 0) - return; - - /* - * With valid RC-5 we can get up to two consecutive half-bits in a - * single pulse measurment. Experiments have shown that the duration - * of a half-bit can vary. Make sure we always end up with an even - * number of quarter bits at the same level (mark or space). - */ - ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; - quarterbits = ns_pulse / rc5_quarterbit_ns; - if (quarterbits & 1) - quarterbits++; - halfbits = quarterbits / 2; - - for (i = 0; i < halfbits; i++) { - ir_input->last_bit++; - ir_input->code |= (level << ir_input->last_bit); - - if (ir_input->last_bit >= RC5_HALF_BITS-1) { - ir_input->active = 0; - cx23885_input_process_raw_rc5(dev); - /* - * If level is 1, a leading mark is invalid for RC5. - * If level is 0, we scan past extra intial space. - * Either way we don't want to reactivate collecting - * marks or spaces here with any left over half-bits. - */ - break; - } - } -} - -static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev, - bool add_eom) +static void cx23885_input_process_measurements(struct cx23885_dev *dev, + bool overrun) { - struct card_ir *ir_input = dev->ir_input; - struct ir_input_state *ir_input_state = &ir_input->ir; + struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir; - u32 ns_pulse[RC5_HALF_BITS+1]; - ssize_t num = 0; + ssize_t num; int count, i; + bool handle = false; + struct ir_raw_event ir_core_event[64]; do { - v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse, - sizeof(ns_pulse), &num); + num = 0; + v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event, + sizeof(ir_core_event), &num); - count = num / sizeof(u32); + count = num / sizeof(struct ir_raw_event); - /* Append an end of Rx seq, if the caller requested */ - if (add_eom && count < ARRAY_SIZE(ns_pulse)) { - ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; - count++; + for (i = 0; i < count; i++) { + ir_raw_event_store(kernel_ir->inp_dev, + &ir_core_event[i]); + handle = true; } - - /* Just drain the Rx FIFO, if we're called, but not RC-5 */ - if (ir_input_state->ir_type != IR_TYPE_RC5) - continue; - - for (i = 0; i < count; i++) - cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]); } while (num != 0); + + if (overrun) + ir_raw_event_reset(kernel_ir->inp_dev); + else if (handle) + ir_raw_event_handle(kernel_ir->inp_dev); } void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) @@ -205,8 +85,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: /* - * The only board we handle right now. However other boards + * The only boards we handle right now. However other boards * using the CX2388x integrated IR controller should be similar */ break; @@ -230,7 +112,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) } if (data_available) - cx23885_input_process_pulse_widths_rc5(dev, overrun); + cx23885_input_process_measurements(dev, overrun); if (overrun) { /* If there was a FIFO overrun, clear & restart the device */ @@ -241,38 +123,20 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) } } -static void cx23885_input_ir_start(struct cx23885_dev *dev) +static int cx23885_input_ir_start(struct cx23885_dev *dev) { - struct card_ir *ir_input = dev->ir_input; - struct ir_input_state *ir_input_state = &ir_input->ir; struct v4l2_subdev_ir_parameters params; if (dev->sd_ir == NULL) - return; + return -ENODEV; atomic_set(&dev->ir_input_stopping, 0); - /* keyup timer set up, if needed */ - switch (dev->board) { - case CX23885_BOARD_HAUPPAUGE_HVR1850: - case CX23885_BOARD_HAUPPAUGE_HVR1290: - setup_timer(&ir_input->timer_keyup, - ir_rc5_timer_keyup, /* Not actually RC-5 specific */ - (unsigned long) ir_input); - if (ir_input_state->ir_type == IR_TYPE_RC5) { - /* - * RC-5 repeats a held key every - * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms - */ - ir_input->rc5_key_timeout = 115; - } - break; - } - v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_HAUPPAUGE_HVR1250: /* * The IR controller on this board only returns pulse widths. * Any other mode setting will fail to set up the device. @@ -295,15 +159,56 @@ static void cx23885_input_ir_start(struct cx23885_dev *dev) * mark is received as low logic level; * falling edges are detected as rising edges; etc. */ - params.invert = true; + params.invert_level = true; + break; + case CX23885_BOARD_TEVII_S470: + /* + * The IR controller on this board only returns pulse widths. + * Any other mode setting will fail to set up the device. + */ + params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + params.enable = true; + params.interrupt_enable = true; + params.shutdown = false; + + /* Setup for a standard NEC protocol */ + params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */ + params.carrier_range_lower = 33000; /* Hz */ + params.carrier_range_upper = 43000; /* Hz */ + params.duty_cycle = 33; /* percent, 33 percent for NEC */ + + /* + * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units + * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns + */ + params.max_pulse_width = 12378022; /* ns */ + + /* + * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit + * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns + */ + params.noise_filter_min_width = 351648; /* ns */ + + params.modulation = false; + params.invert_level = true; break; } v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + return 0; +} + +static int cx23885_input_ir_open(void *priv) +{ + struct cx23885_kernel_ir *kernel_ir = priv; + + if (kernel_ir->cx == NULL) + return -ENODEV; + + return cx23885_input_ir_start(kernel_ir->cx); } static void cx23885_input_ir_stop(struct cx23885_dev *dev) { - struct card_ir *ir_input = dev->ir_input; struct v4l2_subdev_ir_parameters params; if (dev->sd_ir == NULL) @@ -327,21 +232,26 @@ static void cx23885_input_ir_stop(struct cx23885_dev *dev) } flush_scheduled_work(); +} - switch (dev->board) { - case CX23885_BOARD_HAUPPAUGE_HVR1850: - case CX23885_BOARD_HAUPPAUGE_HVR1290: - del_timer_sync(&ir_input->timer_keyup); - break; - } +static void cx23885_input_ir_close(void *priv) +{ + struct cx23885_kernel_ir *kernel_ir = priv; + + if (kernel_ir->cx != NULL) + cx23885_input_ir_stop(kernel_ir->cx); } int cx23885_input_init(struct cx23885_dev *dev) { - struct card_ir *ir; - struct input_dev *input_dev; - char *ir_codes = NULL; - int ir_type, ir_addr, ir_start; + struct cx23885_kernel_ir *kernel_ir; + struct input_dev *inp_dev; + struct ir_dev_props *props; + + char *rc_map; + enum rc_driver_type driver_type; + unsigned long allowed_protos; + int ret; /* @@ -354,53 +264,67 @@ int cx23885_input_init(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: - /* Parameters for the grey Hauppauge remote for the HVR-1850 */ - ir_codes = RC_MAP_HAUPPAUGE_NEW; - ir_type = IR_TYPE_RC5; - ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ - ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ + case CX23885_BOARD_HAUPPAUGE_HVR1250: + /* Integrated CX2388[58] IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = IR_TYPE_ALL; + /* The grey Hauppauge RC-5 remote */ + rc_map = RC_MAP_RC5_HAUPPAUGE_NEW; break; - } - if (ir_codes == NULL) + case CX23885_BOARD_TEVII_S470: + /* Integrated CX23885 IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = IR_TYPE_ALL; + /* A guess at the remote */ + rc_map = RC_MAP_TEVII_NEC; + break; + default: return -ENODEV; - - ir = kzalloc(sizeof(*ir), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ir || !input_dev) { - ret = -ENOMEM; - goto err_out_free; } - ir->dev = input_dev; - ir->addr = ir_addr; - ir->start = ir_start; + /* cx23885 board instance kernel IR state */ + kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL); + if (kernel_ir == NULL) + return -ENOMEM; - /* init input device */ - snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)", - cx23885_boards[dev->board].name); - snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + kernel_ir->cx = dev; + kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)", + cx23885_boards[dev->board].name); + kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0", + pci_name(dev->pci)); - ret = ir_input_init(input_dev, &ir->ir, ir_type); - if (ret < 0) + /* input device */ + inp_dev = input_allocate_device(); + if (inp_dev == NULL) { + ret = -ENOMEM; goto err_out_free; + } - input_dev->name = ir->name; - input_dev->phys = ir->phys; - input_dev->id.bustype = BUS_PCI; - input_dev->id.version = 1; + kernel_ir->inp_dev = inp_dev; + inp_dev->name = kernel_ir->name; + inp_dev->phys = kernel_ir->phys; + inp_dev->id.bustype = BUS_PCI; + inp_dev->id.version = 1; if (dev->pci->subsystem_vendor) { - input_dev->id.vendor = dev->pci->subsystem_vendor; - input_dev->id.product = dev->pci->subsystem_device; + inp_dev->id.vendor = dev->pci->subsystem_vendor; + inp_dev->id.product = dev->pci->subsystem_device; } else { - input_dev->id.vendor = dev->pci->vendor; - input_dev->id.product = dev->pci->device; + inp_dev->id.vendor = dev->pci->vendor; + inp_dev->id.product = dev->pci->device; } - input_dev->dev.parent = &dev->pci->dev; - - dev->ir_input = ir; - cx23885_input_ir_start(dev); - - ret = ir_input_register(ir->dev, ir_codes, NULL, MODULE_NAME); + inp_dev->dev.parent = &dev->pci->dev; + + /* kernel ir device properties */ + props = &kernel_ir->props; + props->driver_type = driver_type; + props->allowed_protos = allowed_protos; + props->priv = kernel_ir; + props->open = cx23885_input_ir_open; + props->close = cx23885_input_ir_close; + + /* Go */ + dev->kernel_ir = kernel_ir; + ret = ir_input_register(inp_dev, rc_map, props, MODULE_NAME); if (ret) goto err_out_stop; @@ -408,9 +332,12 @@ int cx23885_input_init(struct cx23885_dev *dev) err_out_stop: cx23885_input_ir_stop(dev); - dev->ir_input = NULL; + dev->kernel_ir = NULL; + /* TODO: double check clean-up of kernel_ir->inp_dev */ err_out_free: - kfree(ir); + kfree(kernel_ir->phys); + kfree(kernel_ir->name); + kfree(kernel_ir); return ret; } @@ -419,9 +346,11 @@ void cx23885_input_fini(struct cx23885_dev *dev) /* Always stop the IR hardware from generating interrupts */ cx23885_input_ir_stop(dev); - if (dev->ir_input == NULL) + if (dev->kernel_ir == NULL) return; - ir_input_unregister(dev->ir_input->dev); - kfree(dev->ir_input); - dev->ir_input = NULL; + ir_input_unregister(dev->kernel_ir->inp_dev); + kfree(dev->kernel_ir->phys); + kfree(dev->kernel_ir->name); + kfree(dev->kernel_ir); + dev->kernel_ir = NULL; } diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c index 9a677eb080a..7125247dd25 100644 --- a/drivers/media/video/cx23885/cx23885-ir.c +++ b/drivers/media/video/cx23885/cx23885-ir.c @@ -53,7 +53,7 @@ void cx23885_ir_rx_work_handler(struct work_struct *work) if (events == 0) return; - if (dev->ir_input) + if (dev->kernel_ir) cx23885_input_rx_work_handler(dev, events); } @@ -72,7 +72,7 @@ void cx23885_ir_tx_work_handler(struct work_struct *work) } -/* Called in an IRQ context */ +/* Possibly called in an IRQ context */ void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) { struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); @@ -86,10 +86,18 @@ void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications); if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN) set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications); - schedule_work(&dev->ir_rx_work); + + /* + * For the integrated AV core, we are already in a workqueue context. + * For the CX23888 integrated IR, we are in an interrupt context. + */ + if (sd == dev->sd_cx25840) + cx23885_ir_rx_work_handler(&dev->ir_rx_work); + else + schedule_work(&dev->ir_rx_work); } -/* Called in an IRQ context */ +/* Possibly called in an IRQ context */ void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) { struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); @@ -97,5 +105,13 @@ void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ) set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications); - schedule_work(&dev->ir_tx_work); + + /* + * For the integrated AV core, we are already in a workqueue context. + * For the CX23888 integrated IR, we are in an interrupt context. + */ + if (sd == dev->sd_cx25840) + cx23885_ir_tx_work_handler(&dev->ir_tx_work); + else + schedule_work(&dev->ir_tx_work); } diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index c0bc9a06895..a28772db11f 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -213,6 +213,7 @@ Channel manager Data Structure entry = 20 DWORD #define DEV_CNTRL2 0x00040000 #define PCI_MSK_IR (1 << 28) +#define PCI_MSK_AV_CORE (1 << 27) #define PCI_MSK_GPIO1 (1 << 24) #define PCI_MSK_GPIO0 (1 << 23) #define PCI_MSK_APB_DMA (1 << 12) diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c index 708a8c766d1..c0b60382ad1 100644 --- a/drivers/media/video/cx23885/cx23885-vbi.c +++ b/drivers/media/video/cx23885/cx23885-vbi.c @@ -74,7 +74,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, q->count = 1; /* enable irqs */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01); + cx23885_irq_add_enable(dev, 0x01); cx_set(VID_A_INT_MSK, 0x000022); /* start dma */ diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 4e44dcda387..da66e5f8d91 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -441,7 +441,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, q->count = 1; /* enable irq */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01); + cx23885_irq_add_enable(dev, 0x01); cx_set(VID_A_INT_MSK, 0x000011); /* start dma */ @@ -1205,6 +1205,21 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + + printk(KERN_INFO + "%s/0: ============ START LOG STATUS ============\n", + dev->name); + call_all(dev, core, log_status); + printk(KERN_INFO + "%s/0: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qctrl) { @@ -1410,6 +1425,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_log_status = vidioc_log_status, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, @@ -1449,7 +1465,7 @@ static const struct v4l2_file_operations radio_fops = { void cx23885_video_unregister(struct cx23885_dev *dev) { dprintk(1, "%s()\n", __func__); - cx_clear(PCI_INT_MSK, 1); + cx23885_irq_remove(dev, 0x01); if (dev->video_dev) { if (video_is_registered(dev->video_dev)) @@ -1486,7 +1502,8 @@ int cx23885_video_register(struct cx23885_dev *dev) VID_A_DMA_CTL, 0x11, 0x00); /* Don't enable VBI yet */ - cx_set(PCI_INT_MSK, 1); + + cx23885_irq_add_enable(dev, 0x01); if (TUNER_ABSENT != dev->tuner_type) { struct v4l2_subdev *sd = NULL; diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 8d6a55e54ee..ed94b17dd8a 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -30,6 +30,7 @@ #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> #include <media/videobuf-dvb.h> +#include <media/ir-core.h> #include "btcx-risc.h" #include "cx23885-reg.h" @@ -304,6 +305,15 @@ struct cx23885_tsport { void *port_priv; }; +struct cx23885_kernel_ir { + struct cx23885_dev *cx; + char *name; + char *phys; + + struct input_dev *inp_dev; + struct ir_dev_props props; +}; + struct cx23885_dev { atomic_t refcount; struct v4l2_device v4l2_dev; @@ -315,6 +325,7 @@ struct cx23885_dev { u32 __iomem *lmmio; u8 __iomem *bmmio; int pci_irqmask; + spinlock_t pci_irqmask_lock; /* protects mask reg too */ int hwrevision; /* This valud is board specific and is used to configure the @@ -355,6 +366,7 @@ struct cx23885_dev { unsigned char radio_addr; unsigned int has_radio; struct v4l2_subdev *sd_cx25840; + struct work_struct cx25840_work; /* Infrared */ struct v4l2_subdev *sd_ir; @@ -363,7 +375,7 @@ struct cx23885_dev { struct work_struct ir_tx_work; unsigned long ir_tx_notifications; - struct card_ir *ir_input; + struct cx23885_kernel_ir *kernel_ir; atomic_t ir_input_stopping; /* V4l */ @@ -393,7 +405,8 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) #define call_all(dev, o, f, args...) \ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) -#define CX23885_HW_888_IR (1 << 0) +#define CX23885_HW_888_IR (1 << 0) +#define CX23885_HW_AV_CORE (1 << 1) #define call_hw(dev, grpid, o, f, args...) \ v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args) @@ -474,6 +487,10 @@ extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); +extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask); +extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask); +extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask); +extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask); /* ----------------------------------------------------------- */ /* cx23885-cards.c */ diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c index f63d378257a..2502a0a6709 100644 --- a/drivers/media/video/cx23885/cx23888-ir.c +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -26,6 +26,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/ir-core.h> #include "cx23885.h" @@ -60,6 +61,8 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]"); #define CNTRL_CPL 0x00001000 #define CNTRL_LBM 0x00002000 #define CNTRL_R 0x00004000 +/* CX23888 specific control flag */ +#define CNTRL_IVO 0x00008000 #define CX23888_IR_TXCLK_REG 0x170004 #define TXCLK_TCD 0x0000FFFF @@ -111,8 +114,18 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]"); #define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ #define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2) -#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32)) -#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32)) +/* + * We use this union internally for convenience, but callers to tx_write + * and rx_read will be expecting records of type struct ir_raw_event. + * Always ensure the size of this union is dictated by struct ir_raw_event. + */ +union cx23888_ir_fifo_rec { + u32 hw_fifo_data; + struct ir_raw_event ir_core_data; +}; + +#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec)) +#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec)) struct cx23888_ir_state { struct v4l2_subdev sd; @@ -423,6 +436,13 @@ static inline void control_tx_polarity_invert(struct cx23885_dev *dev, invert ? CNTRL_CPL : 0); } +static inline void control_tx_level_invert(struct cx23885_dev *dev, + bool invert) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO, + invert ? CNTRL_IVO : 0); +} + /* * IR Rx & Tx Clock Register helpers */ @@ -449,8 +469,8 @@ static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, { u64 pulse_clocks; - if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) - ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; pulse_clocks = ns_to_pulse_clocks(ns); *divider = pulse_clocks_to_clock_divider(pulse_clocks); cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); @@ -462,8 +482,8 @@ static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, { u64 pulse_clocks; - if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) - ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; pulse_clocks = ns_to_pulse_clocks(ns); *divider = pulse_clocks_to_clock_divider(pulse_clocks); cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); @@ -526,8 +546,8 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); - u32 rx_data[FIFO_RX_DEPTH]; - int i, j, k; + union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; + unsigned int i, j, k; u32 events, v; int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; @@ -588,11 +608,12 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, for (j = 0; (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG); - rx_data[i++] = v & ~FIFO_RX_NDV; + rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; + i++; } if (i == 0) break; - j = i * sizeof(u32); + j = i * sizeof(union cx23888_ir_fifo_rec); k = kfifo_in_locked(&state->rx_kfifo, (unsigned char *) rx_data, j, &state->rx_kfifo_lock); @@ -651,10 +672,11 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, u16 divider = (u16) atomic_read(&state->rxclk_divider); unsigned int i, n; - u32 *p; - u32 u, v; + union cx23888_ir_fifo_rec *p; + unsigned u, v; - n = count / sizeof(u32) * sizeof(u32); + n = count / sizeof(union cx23888_ir_fifo_rec) + * sizeof(union cx23888_ir_fifo_rec); if (n == 0) { *num = 0; return 0; @@ -662,26 +684,28 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock); - n /= sizeof(u32); - *num = n * sizeof(u32); + n /= sizeof(union cx23888_ir_fifo_rec); + *num = n * sizeof(union cx23888_ir_fifo_rec); + + for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { - for (p = (u32 *) buf, i = 0; i < n; p++, i++) { - if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { - *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + /* Assume RTO was because of no IR light input */ + u = 0; v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); - continue; + } else { + u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; + if (invert) + u = u ? 0 : 1; } - u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0; - if (invert) - u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK; + v = (unsigned) pulse_width_count_to_ns( + (u16) (p->hw_fifo_data & FIFO_RXTX), divider); + if (v > IR_MAX_DURATION) + v = IR_MAX_DURATION; - v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX), - divider); - if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) - v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1; - - *p = u | v; + p->ir_core_data.pulse = u; + p->ir_core_data.duration = v; v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n", v, u ? "mark" : "space"); @@ -740,7 +764,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; - o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + o->bytes_per_data_element = p->bytes_per_data_element + = sizeof(union cx23888_ir_fifo_rec); /* Before we tweak the hardware, we have to disable the receiver */ irqenable_rx(dev, 0); @@ -762,12 +787,15 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, &p->carrier_range_upper); o->carrier_range_lower = p->carrier_range_lower; o->carrier_range_upper = p->carrier_range_upper; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); } else { p->max_pulse_width = rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width, &rxclk_divider); - o->max_pulse_width = p->max_pulse_width; } + o->max_pulse_width = p->max_pulse_width; atomic_set(&state->rxclk_divider, rxclk_divider); p->noise_filter_min_width = @@ -782,8 +810,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH); - o->invert = p->invert; - atomic_set(&state->rx_invert, p->invert); + o->invert_level = p->invert_level; + atomic_set(&state->rx_invert, p->invert_level); o->interrupt_enable = p->interrupt_enable; o->enable = p->enable; @@ -864,7 +892,8 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; - o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + o->bytes_per_data_element = p->bytes_per_data_element + = sizeof(union cx23888_ir_fifo_rec); /* Before we tweak the hardware, we have to disable the transmitter */ irqenable_tx(dev, 0); @@ -880,12 +909,15 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle); o->duty_cycle = p->duty_cycle; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); } else { p->max_pulse_width = txclk_tx_s_max_pulse_width(dev, p->max_pulse_width, &txclk_divider); - o->max_pulse_width = p->max_pulse_width; } + o->max_pulse_width = p->max_pulse_width; atomic_set(&state->txclk_divider, txclk_divider); p->resolution = clock_divider_to_resolution(txclk_divider); @@ -894,8 +926,11 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, /* FIXME - make this dependent on resolution for better performance */ control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY); - control_tx_polarity_invert(dev, p->invert); - o->invert = p->invert; + control_tx_polarity_invert(dev, p->invert_carrier_sense); + o->invert_carrier_sense = p->invert_carrier_sense; + + control_tx_level_invert(dev, p->invert_level); + o->invert_level = p->invert_level; o->interrupt_enable = p->interrupt_enable; o->enable = p->enable; @@ -988,12 +1023,10 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd) "-%1d/+%1d, %u to %u Hz\n", i, j, clock_divider_to_freq(rxclk, 16 + j), clock_divider_to_freq(rxclk, 16 - i)); - } else { - v4l2_info(sd, "\tMax measurable pulse width: %u us, " - "%llu ns\n", - pulse_width_count_to_us(FIFO_RXTX, rxclk), - pulse_width_count_to_ns(FIFO_RXTX, rxclk)); } + v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); v4l2_info(sd, "\tLow pass filter: %s\n", filtr ? "enabled" : "disabled"); if (filtr) @@ -1025,19 +1058,20 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd) cntrl & CNTRL_TFE ? "enabled" : "disabled"); v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", cntrl & CNTRL_TIC ? "not empty" : "half full or less"); - v4l2_info(sd, "\tSignal polarity: %s\n", - cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark"); + v4l2_info(sd, "\tOutput pin level inversion %s\n", + cntrl & CNTRL_IVO ? "yes" : "no"); + v4l2_info(sd, "\tCarrier polarity: %s\n", + cntrl & CNTRL_CPL ? "space:burst mark:noburst" + : "space:noburst mark:burst"); if (cntrl & CNTRL_MOD) { v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", clock_divider_to_carrier_freq(txclk)); v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", cduty + 1); - } else { - v4l2_info(sd, "\tMax pulse width: %u us, " - "%llu ns\n", - pulse_width_count_to_us(FIFO_RXTX, txclk), - pulse_width_count_to_ns(FIFO_RXTX, txclk)); } + v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); v4l2_info(sd, "\tBusy: %s\n", stats & STATS_TBY ? "yes" : "no"); v4l2_info(sd, "\tFIFO service requested: %s\n", @@ -1111,11 +1145,10 @@ static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = { .g_register = cx23888_ir_g_register, .s_register = cx23888_ir_s_register, #endif + .interrupt_service_routine = cx23888_ir_irq_handler, }; static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = { - .interrupt_service_routine = cx23888_ir_irq_handler, - .rx_read = cx23888_ir_rx_read, .rx_g_parameters = cx23888_ir_rx_g_parameters, .rx_s_parameters = cx23888_ir_rx_s_parameters, @@ -1131,7 +1164,7 @@ static const struct v4l2_subdev_ops cx23888_ir_controller_ops = { }; static const struct v4l2_subdev_ir_parameters default_rx_params = { - .bytes_per_data_element = sizeof(u32), + .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec), .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, .enable = false, @@ -1146,11 +1179,11 @@ static const struct v4l2_subdev_ir_parameters default_rx_params = { .noise_filter_min_width = 333333, /* ns */ .carrier_range_lower = 35000, .carrier_range_upper = 37000, - .invert = false, + .invert_level = false, }; static const struct v4l2_subdev_ir_parameters default_tx_params = { - .bytes_per_data_element = sizeof(u32), + .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec), .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, .enable = false, @@ -1160,7 +1193,8 @@ static const struct v4l2_subdev_ir_parameters default_tx_params = { .modulation = true, .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ .duty_cycle = 25, /* 25 % - RC-5 carrier */ - .invert = false, + .invert_level = false, + .invert_carrier_sense = false, }; int cx23888_ir_probe(struct cx23885_dev *dev) diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile index 6e8665be895..2ee96d3973b 100644 --- a/drivers/media/video/cx25840/Makefile +++ b/drivers/media/video/cx25840/Makefile @@ -1,5 +1,5 @@ cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ - cx25840-vbi.o + cx25840-vbi.o cx25840-ir.o obj-$(CONFIG_VIDEO_CX25840) += cx25840.o diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index 45608d50529..6faad34df3a 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -474,33 +474,10 @@ void cx25840_audio_set_path(struct i2c_client *client) cx25840_and_or(client, 0x803, ~0x10, 0x10); } -static int get_volume(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - int vol; - - if (state->unmute_volume >= 0) - return state->unmute_volume; - - /* Volume runs +18dB to -96dB in 1/2dB steps - * change to fit the msp3400 -114dB to +12dB range */ - - /* check PATH1_VOLUME */ - vol = 228 - cx25840_read(client, 0x8d4); - vol = (vol / 2) + 23; - return vol << 9; -} - static void set_volume(struct i2c_client *client, int volume) { - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; - if (state->unmute_volume >= 0) { - state->unmute_volume = volume; - return; - } - /* Convert the volume to msp3400 values (0-127) */ vol = volume >> 9; @@ -517,52 +494,6 @@ static void set_volume(struct i2c_client *client, int volume) cx25840_write(client, 0x8d4, 228 - (vol * 2)); } -static int get_bass(struct i2c_client *client) -{ - /* bass is 49 steps +12dB to -12dB */ - - /* check PATH1_EQ_BASS_VOL */ - int bass = cx25840_read(client, 0x8d9) & 0x3f; - bass = (((48 - bass) * 0xffff) + 47) / 48; - return bass; -} - -static void set_bass(struct i2c_client *client, int bass) -{ - /* PATH1_EQ_BASS_VOL */ - cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); -} - -static int get_treble(struct i2c_client *client) -{ - /* treble is 49 steps +12dB to -12dB */ - - /* check PATH1_EQ_TREBLE_VOL */ - int treble = cx25840_read(client, 0x8db) & 0x3f; - treble = (((48 - treble) * 0xffff) + 47) / 48; - return treble; -} - -static void set_treble(struct i2c_client *client, int treble) -{ - /* PATH1_EQ_TREBLE_VOL */ - cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); -} - -static int get_balance(struct i2c_client *client) -{ - /* balance is 7 bit, 0 to -96dB */ - - /* check PATH1_BAL_LEVEL */ - int balance = cx25840_read(client, 0x8d5) & 0x7f; - /* check PATH1_BAL_LEFT */ - if ((cx25840_read(client, 0x8d5) & 0x80) == 0) - balance = 0x80 - balance; - else - balance = 0x80 + balance; - return balance << 8; -} - static void set_balance(struct i2c_client *client, int balance) { int bal = balance >> 8; @@ -579,31 +510,6 @@ static void set_balance(struct i2c_client *client, int balance) } } -static int get_mute(struct i2c_client *client) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - return state->unmute_volume >= 0; -} - -static void set_mute(struct i2c_client *client, int mute) -{ - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - - if (mute && state->unmute_volume == -1) { - int vol = get_volume(client); - - set_volume(client, 0); - state->unmute_volume = vol; - } - else if (!mute && state->unmute_volume != -1) { - int vol = state->unmute_volume; - - state->unmute_volume = -1; - set_volume(client, vol); - } -} - int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -624,25 +530,31 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) return retval; } -int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); + struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: - ctrl->value = get_volume(client); + if (state->mute->val) + set_volume(client, 0); + else + set_volume(client, state->volume->val); break; case V4L2_CID_AUDIO_BASS: - ctrl->value = get_bass(client); + /* PATH1_EQ_BASS_VOL */ + cx25840_and_or(client, 0x8d9, ~0x3f, + 48 - (ctrl->val * 48 / 0xffff)); break; case V4L2_CID_AUDIO_TREBLE: - ctrl->value = get_treble(client); + /* PATH1_EQ_TREBLE_VOL */ + cx25840_and_or(client, 0x8db, ~0x3f, + 48 - (ctrl->val * 48 / 0xffff)); break; case V4L2_CID_AUDIO_BALANCE: - ctrl->value = get_balance(client); - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(client); + set_balance(client, ctrl->val); break; default: return -EINVAL; @@ -650,28 +562,6 @@ int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - set_volume(client, ctrl->value); - break; - case V4L2_CID_AUDIO_BASS: - set_bass(client, ctrl->value); - break; - case V4L2_CID_AUDIO_TREBLE: - set_treble(client, ctrl->value); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(client, ctrl->value); - break; - case V4L2_CID_AUDIO_MUTE: - set_mute(client, ctrl->value); - break; - default: - return -EINVAL; - } - return 0; -} +const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { + .s_ctrl = cx25840_audio_s_ctrl, +}; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index bb4872b2ceb..86ca8c2359d 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -15,6 +15,9 @@ * * CX23885 support by Steven Toth <stoth@linuxtv.org>. * + * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are + * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net> + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -48,6 +51,28 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); MODULE_LICENSE("GPL"); +#define CX25840_VID_INT_STAT_REG 0x410 +#define CX25840_VID_INT_STAT_BITS 0x0000ffff +#define CX25840_VID_INT_MASK_BITS 0xffff0000 +#define CX25840_VID_INT_MASK_SHFT 16 +#define CX25840_VID_INT_MASK_REG 0x412 + +#define CX23885_AUD_MC_INT_MASK_REG 0x80c +#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000 +#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff +#define CX23885_AUD_MC_INT_STAT_SHFT 16 + +#define CX25840_AUD_INT_CTRL_REG 0x812 +#define CX25840_AUD_INT_STAT_REG 0x813 + +#define CX23885_PIN_CTRL_IRQ_REG 0x123 +#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40 +#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20 +#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10 + +#define CX25840_IR_STATS_REG 0x210 +#define CX25840_IR_IRQEN_REG 0x214 + static int cx25840_debug; module_param_named(debug,cx25840_debug, int, 0644); @@ -80,33 +105,53 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) u8 cx25840_read(struct i2c_client * client, u16 addr) { - u8 buffer[2]; - buffer[0] = addr >> 8; - buffer[1] = addr & 0xff; - - if (i2c_master_send(client, buffer, 2) < 2) - return 0; - - if (i2c_master_recv(client, buffer, 1) < 1) + struct i2c_msg msgs[2]; + u8 tx_buf[2], rx_buf[1]; + + /* Write register address */ + tx_buf[0] = addr >> 8; + tx_buf[1] = addr & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (char *) tx_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = (char *) rx_buf; + + if (i2c_transfer(client->adapter, msgs, 2) < 2) return 0; - return buffer[0]; + return rx_buf[0]; } u32 cx25840_read4(struct i2c_client * client, u16 addr) { - u8 buffer[4]; - buffer[0] = addr >> 8; - buffer[1] = addr & 0xff; - - if (i2c_master_send(client, buffer, 2) < 2) - return 0; - - if (i2c_master_recv(client, buffer, 4) < 4) + struct i2c_msg msgs[2]; + u8 tx_buf[2], rx_buf[4]; + + /* Write register address */ + tx_buf[0] = addr >> 8; + tx_buf[1] = addr & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (char *) tx_buf; + + /* Read data from registers */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 4; + msgs[1].buf = (char *) rx_buf; + + if (i2c_transfer(client->adapter, msgs, 2) < 2) return 0; - return (buffer[3] << 24) | (buffer[2] << 16) | - (buffer[1] << 8) | buffer[0]; + return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) | + rx_buf[0]; } int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, @@ -117,6 +162,14 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, or_value); } +int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, + u32 or_value) +{ + return cx25840_write4(client, addr, + (cx25840_read4(client, addr) & and_mask) | + or_value); +} + /* ----------------------------------------------------------------------- */ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, @@ -124,6 +177,158 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp /* ----------------------------------------------------------------------- */ +static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *p) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i; + u32 pin_ctrl; + u8 gpio_oe, gpio_data, strength; + + pin_ctrl = cx25840_read4(client, 0x120); + gpio_oe = cx25840_read(client, 0x160); + gpio_data = cx25840_read(client, 0x164); + + for (i = 0; i < n; i++) { + strength = p[i].strength; + if (strength > CX25840_PIN_DRIVE_FAST) + strength = CX25840_PIN_DRIVE_FAST; + + switch (p[i].pin) { + case CX23885_PIN_IRQ_N_GPIO16: + if (p[i].function != CX23885_PAD_IRQ_N) { + /* GPIO16 */ + pin_ctrl &= ~(0x1 << 25); + } else { + /* IRQ_N */ + if (p[i].flags & + (V4L2_SUBDEV_IO_PIN_DISABLE | + V4L2_SUBDEV_IO_PIN_INPUT)) { + pin_ctrl &= ~(0x1 << 25); + } else { + pin_ctrl |= (0x1 << 25); + } + if (p[i].flags & + V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) { + pin_ctrl &= ~(0x1 << 24); + } else { + pin_ctrl |= (0x1 << 24); + } + } + break; + case CX23885_PIN_IR_RX_GPIO19: + if (p[i].function != CX23885_PAD_GPIO19) { + /* IR_RX */ + gpio_oe |= (0x1 << 0); + pin_ctrl &= ~(0x3 << 18); + pin_ctrl |= (strength << 18); + } else { + /* GPIO19 */ + gpio_oe &= ~(0x1 << 0); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 0); + gpio_data |= ((p[i].value & 0x1) << 0); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_IR_TX_GPIO20: + if (p[i].function != CX23885_PAD_GPIO20) { + /* IR_TX */ + gpio_oe |= (0x1 << 1); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE) + pin_ctrl &= ~(0x1 << 10); + else + pin_ctrl |= (0x1 << 10); + pin_ctrl &= ~(0x3 << 18); + pin_ctrl |= (strength << 18); + } else { + /* GPIO20 */ + gpio_oe &= ~(0x1 << 1); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 1); + gpio_data |= ((p[i].value & 0x1) << 1); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_SDAT_GPIO21: + if (p[i].function != CX23885_PAD_GPIO21) { + /* I2S_SDAT */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 2); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO21 */ + gpio_oe &= ~(0x1 << 2); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 2); + gpio_data |= ((p[i].value & 0x1) << 2); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_WCLK_GPIO22: + if (p[i].function != CX23885_PAD_GPIO22) { + /* I2S_WCLK */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 3); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO22 */ + gpio_oe &= ~(0x1 << 3); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 3); + gpio_data |= ((p[i].value & 0x1) << 3); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + case CX23885_PIN_I2S_BCLK_GPIO23: + if (p[i].function != CX23885_PAD_GPIO23) { + /* I2S_BCLK */ + /* TODO: Input or Output config */ + gpio_oe |= (0x1 << 4); + pin_ctrl &= ~(0x3 << 22); + pin_ctrl |= (strength << 22); + } else { + /* GPIO23 */ + gpio_oe &= ~(0x1 << 4); + if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { + gpio_data &= ~(0x1 << 4); + gpio_data |= ((p[i].value & 0x1) << 4); + } + pin_ctrl &= ~(0x3 << 12); + pin_ctrl |= (strength << 12); + } + break; + } + } + + cx25840_write(client, 0x164, gpio_data); + cx25840_write(client, 0x160, gpio_oe); + cx25840_write4(client, 0x120, pin_ctrl); + return 0; +} + +static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *pincfg) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx2388x(state)) + return cx23885_s_io_pin_config(sd, n, pincfg); + return 0; +} + +/* ----------------------------------------------------------------------- */ + static void init_dll1(struct i2c_client *client) { /* This is the Hauppauge sequence used to @@ -420,6 +625,13 @@ static void cx23885_initialize(struct i2c_client *client) /* start microcontroller */ cx25840_and_or(client, 0x803, ~0x10, 0x10); + + /* Disable and clear video interrupts - we don't use them */ + cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff); + + /* Disable and clear audio interrupts - we don't use them */ + cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff); + cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); } /* ----------------------------------------------------------------------- */ @@ -909,102 +1121,29 @@ static int set_v4lstd(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) { - struct cx25840_state *state = to_state(sd); + struct v4l2_subdev *sd = to_sd(ctrl); struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { - case CX25840_CID_ENABLE_PVR150_WORKAROUND: - state->pvr150_workaround = ctrl->value; - set_input(client, state->vid_input, state->aud_input); - break; - case V4L2_CID_BRIGHTNESS: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx25840_write(client, 0x414, ctrl->value - 128); + cx25840_write(client, 0x414, ctrl->val - 128); break; case V4L2_CID_CONTRAST: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx25840_write(client, 0x415, ctrl->value << 1); + cx25840_write(client, 0x415, ctrl->val << 1); break; case V4L2_CID_SATURATION: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", - ctrl->value); - return -ERANGE; - } - - cx25840_write(client, 0x420, ctrl->value << 1); - cx25840_write(client, 0x421, ctrl->value << 1); + cx25840_write(client, 0x420, ctrl->val << 1); + cx25840_write(client, 0x421, ctrl->val << 1); break; case V4L2_CID_HUE: - if (ctrl->value < -128 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); - return -ERANGE; - } - - cx25840_write(client, 0x422, ctrl->value); + cx25840_write(client, 0x422, ctrl->val); break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_MUTE: - if (is_cx2583x(state)) - return -EINVAL; - return cx25840_audio_s_ctrl(sd, ctrl); - - default: - return -EINVAL; - } - - return 0; -} - -static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case CX25840_CID_ENABLE_PVR150_WORKAROUND: - ctrl->value = state->pvr150_workaround; - break; - case V4L2_CID_BRIGHTNESS: - ctrl->value = (s8)cx25840_read(client, 0x414) + 128; - break; - case V4L2_CID_CONTRAST: - ctrl->value = cx25840_read(client, 0x415) >> 1; - break; - case V4L2_CID_SATURATION: - ctrl->value = cx25840_read(client, 0x420) >> 1; - break; - case V4L2_CID_HUE: - ctrl->value = (s8)cx25840_read(client, 0x422); - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_MUTE: - if (is_cx2583x(state)) - return -EINVAL; - return cx25840_audio_g_ctrl(sd, ctrl); default: return -EINVAL; } @@ -1163,8 +1302,6 @@ static void log_audio_status(struct i2c_client *client) default: p = "not defined"; } v4l_info(client, "Detected audio standard: %s\n", p); - v4l_info(client, "Audio muted: %s\n", - (state->unmute_volume >= 0) ? "yes" : "no"); v4l_info(client, "Audio microcontroller: %s\n", (download_ctl & 0x10) ? ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); @@ -1381,40 +1518,6 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct cx25840_state *state = to_state(sd); - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - default: - break; - } - if (is_cx2583x(state)) - return -EINVAL; - - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, - 65535 / 100, state->default_volume); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - default: - return -EINVAL; - } - return -EINVAL; -} - static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct cx25840_state *state = to_state(sd); @@ -1576,24 +1679,134 @@ static int cx25840_log_status(struct v4l2_subdev *sd) log_video_status(client); if (!is_cx2583x(state)) log_audio_status(client); + cx25840_ir_log_status(sd); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + +static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (platform_data) { + struct cx25840_platform_data *pdata = platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround; + set_input(client, state->vid_input, state->aud_input); + } return 0; } +static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *c = v4l2_get_subdevdata(sd); + u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en; + u32 vid_stat, aud_mc_stat; + bool block_handled; + int ret = 0; + + irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); + v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n", + irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); + + if ((is_cx23885(state) || is_cx23887(state))) { + ir_stat = cx25840_read(c, CX25840_IR_STATS_REG); + ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core ir IRQ status: %#04x disables: %#04x\n", + ir_stat, ir_en); + if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) { + block_handled = false; + ret = cx25840_ir_irq_handler(sd, + status, &block_handled); + if (block_handled) + *handled = true; + } + } + + aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG); + aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core audio IRQ status: %#04x disables: %#04x\n", + aud_stat, aud_en); + aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core audio MC IRQ status: %#06x enables: %#06x\n", + aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT, + aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS); + if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) { + if (aud_stat) { + cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat); + *handled = true; + } + } + + vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG); + v4l_dbg(2, cx25840_debug, c, + "AV Core video IRQ status: %#06x disables: %#06x\n", + vid_stat & CX25840_VID_INT_STAT_BITS, + vid_stat >> CX25840_VID_INT_MASK_SHFT); + if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) { + if (vid_stat & CX25840_VID_INT_STAT_BITS) { + cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat); + *handled = true; + } + } + + irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); + v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n", + irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", + irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); + + return ret; +} + +static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx25840_state *state = to_state(sd); + + *handled = false; + + /* Only support the CX2388[578] AV Core for now */ + if (is_cx2388x(state)) + return cx23885_irq_handler(sd, status, handled); + + return -ENODEV; +} + /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { + .s_ctrl = cx25840_s_ctrl, +}; + static const struct v4l2_subdev_core_ops cx25840_core_ops = { .log_status = cx25840_log_status, + .s_config = cx25840_s_config, .g_chip_ident = cx25840_g_chip_ident, - .g_ctrl = cx25840_g_ctrl, - .s_ctrl = cx25840_s_ctrl, - .queryctrl = cx25840_queryctrl, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = cx25840_s_std, .reset = cx25840_reset, .load_fw = cx25840_load_fw, + .s_io_pin_config = common_s_io_pin_config, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cx25840_g_register, .s_register = cx25840_s_register, #endif + .interrupt_service_routine = cx25840_irq_handler, }; static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { @@ -1628,6 +1841,7 @@ static const struct v4l2_subdev_ops cx25840_ops = { .audio = &cx25840_audio_ops, .video = &cx25840_video_ops, .vbi = &cx25840_vbi_ops, + .ir = &cx25840_ir_ops, }; /* ----------------------------------------------------------------------- */ @@ -1675,6 +1889,7 @@ static int cx25840_probe(struct i2c_client *client, { struct cx25840_state *state; struct v4l2_subdev *sd; + int default_volume; u32 id = V4L2_IDENT_NONE; u16 device_id; @@ -1718,6 +1933,7 @@ static int cx25840_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &cx25840_ops); + switch (id) { case V4L2_IDENT_CX23885_AV: v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", @@ -1762,22 +1978,62 @@ static int cx25840_probe(struct i2c_client *client, state->audclk_freq = 48000; state->pvr150_workaround = 0; state->audmode = V4L2_TUNER_MODE_LANG1; - state->unmute_volume = -1; - state->default_volume = 228 - cx25840_read(client, 0x8d4); - state->default_volume = ((state->default_volume / 2) + 23) << 9; state->vbi_line_offset = 8; state->id = id; state->rev = device_id; + v4l2_ctrl_handler_init(&state->hdl, 9); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + if (!is_cx2583x(state)) { + default_volume = 228 - cx25840_read(client, 0x8d4); + default_volume = ((default_volume / 2) + 23) << 9; + + state->volume = v4l2_ctrl_new_std(&state->hdl, + &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, + 0, 65335, 65535 / 100, default_volume); + state->mute = v4l2_ctrl_new_std(&state->hdl, + &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, 32768); + } + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_cluster(2, &state->volume); + v4l2_ctrl_handler_setup(&state->hdl); + cx25840_ir_probe(sd); return 0; } static int cx25840_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + cx25840_ir_remove(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 04393b97156..bd4ada28b49 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -24,19 +24,20 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #include <linux/i2c.h> -/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is - present in Hauppauge PVR-150 (and possibly PVR-500) cards that have - certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The - audio autodetect fails on some channels for these models and the workaround - is to select the audio standard explicitly. Many thanks to Hauppauge for - providing this information. */ -#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) +struct cx25840_ir_state; struct cx25840_state { struct i2c_client *c; struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* volume cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + }; int pvr150_workaround; int radio; v4l2_std_id std; @@ -44,14 +45,13 @@ struct cx25840_state { enum cx25840_audio_input aud_input; u32 audclk_freq; int audmode; - int unmute_volume; /* -1 if not muted */ - int default_volume; int vbi_line_offset; u32 id; u32 rev; int is_initialized; wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ struct work_struct fw_work; /* work entry for fw load */ + struct cx25840_ir_state *ir_state; }; static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) @@ -59,6 +59,11 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct cx25840_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; +} + static inline bool is_cx2583x(struct cx25840_state *state) { return state->id == V4L2_IDENT_CX25836 || @@ -77,6 +82,21 @@ static inline bool is_cx2388x(struct cx25840_state *state) state->id == V4L2_IDENT_CX23888_AV; } +static inline bool is_cx23885(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV; +} + +static inline bool is_cx23887(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23887_AV; +} + +static inline bool is_cx23888(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23888_AV; +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); @@ -84,6 +104,8 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); u8 cx25840_read(struct i2c_client *client, u16 addr); u32 cx25840_read4(struct i2c_client *client, u16 addr); int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); +int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, + u32 or_value); void cx25840_std_setup(struct i2c_client *client); /* ----------------------------------------------------------------------- */ @@ -94,8 +116,8 @@ int cx25840_loadfw(struct i2c_client *client); /* cx25850-audio.c */ void cx25840_audio_set_path(struct i2c_client *client); int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); -int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); -int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + +extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ @@ -104,4 +126,12 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format * int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); +/* ----------------------------------------------------------------------- */ +/* cx25850-ir.c */ +extern const struct v4l2_subdev_ir_ops cx25840_ir_ops; +int cx25840_ir_log_status(struct v4l2_subdev *sd); +int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled); +int cx25840_ir_probe(struct v4l2_subdev *sd); +int cx25840_ir_remove(struct v4l2_subdev *sd); + #endif diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c new file mode 100644 index 00000000000..c2b4c14dc9a --- /dev/null +++ b/drivers/media/video/cx25840/cx25840-ir.c @@ -0,0 +1,1279 @@ +/* + * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores + * + * Integrated Consumer Infrared Controller + * + * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/slab.h> +#include <linux/kfifo.h> +#include <media/cx25840.h> +#include <media/ir-core.h> + +#include "cx25840-core.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages"); + +#define CX25840_IR_REG_BASE 0x200 + +#define CX25840_IR_CNTRL_REG 0x200 +#define CNTRL_WIN_3_3 0x00000000 +#define CNTRL_WIN_4_3 0x00000001 +#define CNTRL_WIN_3_4 0x00000002 +#define CNTRL_WIN_4_4 0x00000003 +#define CNTRL_WIN 0x00000003 +#define CNTRL_EDG_NONE 0x00000000 +#define CNTRL_EDG_FALL 0x00000004 +#define CNTRL_EDG_RISE 0x00000008 +#define CNTRL_EDG_BOTH 0x0000000C +#define CNTRL_EDG 0x0000000C +#define CNTRL_DMD 0x00000010 +#define CNTRL_MOD 0x00000020 +#define CNTRL_RFE 0x00000040 +#define CNTRL_TFE 0x00000080 +#define CNTRL_RXE 0x00000100 +#define CNTRL_TXE 0x00000200 +#define CNTRL_RIC 0x00000400 +#define CNTRL_TIC 0x00000800 +#define CNTRL_CPL 0x00001000 +#define CNTRL_LBM 0x00002000 +#define CNTRL_R 0x00004000 + +#define CX25840_IR_TXCLK_REG 0x204 +#define TXCLK_TCD 0x0000FFFF + +#define CX25840_IR_RXCLK_REG 0x208 +#define RXCLK_RCD 0x0000FFFF + +#define CX25840_IR_CDUTY_REG 0x20C +#define CDUTY_CDC 0x0000000F + +#define CX25840_IR_STATS_REG 0x210 +#define STATS_RTO 0x00000001 +#define STATS_ROR 0x00000002 +#define STATS_RBY 0x00000004 +#define STATS_TBY 0x00000008 +#define STATS_RSR 0x00000010 +#define STATS_TSR 0x00000020 + +#define CX25840_IR_IRQEN_REG 0x214 +#define IRQEN_RTE 0x00000001 +#define IRQEN_ROE 0x00000002 +#define IRQEN_RSE 0x00000010 +#define IRQEN_TSE 0x00000020 +#define IRQEN_MSK 0x00000033 + +#define CX25840_IR_FILTR_REG 0x218 +#define FILTR_LPF 0x0000FFFF + +#define CX25840_IR_FIFO_REG 0x23C +#define FIFO_RXTX 0x0000FFFF +#define FIFO_RXTX_LVL 0x00010000 +#define FIFO_RXTX_RTO 0x0001FFFF +#define FIFO_RX_NDV 0x00020000 +#define FIFO_RX_DEPTH 8 +#define FIFO_TX_DEPTH 8 + +#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ +#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2) + +/* + * We use this union internally for convenience, but callers to tx_write + * and rx_read will be expecting records of type struct ir_raw_event. + * Always ensure the size of this union is dictated by struct ir_raw_event. + */ +union cx25840_ir_fifo_rec { + u32 hw_fifo_data; + struct ir_raw_event ir_core_data; +}; + +#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) +#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) + +struct cx25840_ir_state { + struct i2c_client *c; + + struct v4l2_subdev_ir_parameters rx_params; + struct mutex rx_params_lock; /* protects Rx parameter settings cache */ + atomic_t rxclk_divider; + atomic_t rx_invert; + + struct kfifo rx_kfifo; + spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */ + + struct v4l2_subdev_ir_parameters tx_params; + struct mutex tx_params_lock; /* protects Tx parameter settings cache */ + atomic_t txclk_divider; +}; + +static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + return state ? state->ir_state : NULL; +} + + +/* + * Rx and Tx Clock Divider register computations + * + * Note the largest clock divider value of 0xffff corresponds to: + * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_clock_divider(unsigned int d) +{ + if (d > RXCLK_RCD + 1) + d = RXCLK_RCD; + else if (d < 2) + d = 1; + else + d--; + return (u16) d; +} + +static inline u16 ns_to_clock_divider(unsigned int ns) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int clock_divider_to_ns(unsigned int divider) +{ + /* Period of the Rx or Tx clock in ns */ + return DIV_ROUND_CLOSEST((divider + 1) * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static inline u16 carrier_freq_to_clock_divider(unsigned int freq) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16)); +} + +static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) +{ + return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16); +} + +static inline u16 freq_to_clock_divider(unsigned int freq, + unsigned int rollovers) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers)); +} + +static inline unsigned int clock_divider_to_freq(unsigned int divider, + unsigned int rollovers) +{ + return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, + (divider + 1) * rollovers); +} + +/* + * Low Pass Filter register calculations + * + * Note the largest count value of 0xffff corresponds to: + * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_lpf_count(unsigned int d) +{ + if (d > FILTR_LPF) + d = FILTR_LPF; + else if (d < 4) + d = 0; + return (u16) d; +} + +static inline u16 ns_to_lpf_count(unsigned int ns) +{ + return count_to_lpf_count( + DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int lpf_count_to_ns(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in ns */ + return DIV_ROUND_CLOSEST(count * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static inline unsigned int lpf_count_to_us(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in us */ + return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000); +} + +/* + * FIFO register pulse width count compuations + */ +static u32 clock_divider_to_resolution(u16 divider) +{ + /* + * Resolution is the duration of 1 tick of the readable portion of + * of the pulse width counter as read from the FIFO. The two lsb's are + * not readable, hence the << 2. This function returns ns. + */ + return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, + CX25840_IR_REFCLK_FREQ / 1000000); +} + +static u64 pulse_width_count_to_ns(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ + rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ + if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return n; +} + +#if 0 +/* Keep as we will need this for Transmit functionality */ +static u16 ns_to_pulse_width_count(u32 ns, u16 divider) +{ + u64 n; + u32 d; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not accessable, hence + * the (1 << 2) + */ + n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */ + d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */ + rem = do_div(n, d); + if (rem >= d / 2) + n++; + + if (n > FIFO_RXTX) + n = FIFO_RXTX; + else if (n == 0) + n = 1; + return (u16) n; +} + +#endif +static unsigned int pulse_width_count_to_us(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ + rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ + if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return (unsigned int) n; +} + +/* + * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts + * + * The total pulse clock count is an 18 bit pulse width timer count as the most + * significant part and (up to) 16 bit clock divider count as a modulus. + * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse + * width timer count's least significant bit. + */ +static u64 ns_to_pulse_clocks(u32 ns) +{ + u64 clocks; + u32 rem; + clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ + rem = do_div(clocks, 1000); /* /1000 = cycles */ + if (rem >= 1000 / 2) + clocks++; + return clocks; +} + +static u16 pulse_clocks_to_clock_divider(u64 count) +{ + u32 rem; + + rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + + /* net result needs to be rounded down and decremented by 1 */ + if (count > RXCLK_RCD + 1) + count = RXCLK_RCD; + else if (count < 2) + count = 1; + else + count--; + return (u16) count; +} + +/* + * IR Control Register helpers + */ +enum tx_fifo_watermark { + TX_FIFO_HALF_EMPTY = 0, + TX_FIFO_EMPTY = CNTRL_TIC, +}; + +enum rx_fifo_watermark { + RX_FIFO_HALF_FULL = 0, + RX_FIFO_NOT_EMPTY = CNTRL_RIC, +}; + +static inline void control_tx_irq_watermark(struct i2c_client *c, + enum tx_fifo_watermark level) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level); +} + +static inline void control_rx_irq_watermark(struct i2c_client *c, + enum rx_fifo_watermark level) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level); +} + +static inline void control_tx_enable(struct i2c_client *c, bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), + enable ? (CNTRL_TXE | CNTRL_TFE) : 0); +} + +static inline void control_rx_enable(struct i2c_client *c, bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), + enable ? (CNTRL_RXE | CNTRL_RFE) : 0); +} + +static inline void control_tx_modulation_enable(struct i2c_client *c, + bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD, + enable ? CNTRL_MOD : 0); +} + +static inline void control_rx_demodulation_enable(struct i2c_client *c, + bool enable) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD, + enable ? CNTRL_DMD : 0); +} + +static inline void control_rx_s_edge_detection(struct i2c_client *c, + u32 edge_types) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, + edge_types & CNTRL_EDG_BOTH); +} + +static void control_rx_s_carrier_window(struct i2c_client *c, + unsigned int carrier, + unsigned int *carrier_range_low, + unsigned int *carrier_range_high) +{ + u32 v; + unsigned int c16 = carrier * 16; + + if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { + v = CNTRL_WIN_3_4; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); + } else { + v = CNTRL_WIN_3_3; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); + } + + if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { + v |= CNTRL_WIN_4_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); + } else { + v |= CNTRL_WIN_3_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); + } + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v); +} + +static inline void control_tx_polarity_invert(struct i2c_client *c, + bool invert) +{ + cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL, + invert ? CNTRL_CPL : 0); +} + +/* + * IR Rx & Tx Clock Register helpers + */ +static unsigned int txclk_tx_s_carrier(struct i2c_client *c, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static unsigned int rxclk_rx_s_carrier(struct i2c_client *c, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > IR_MAX_DURATION) + ns = IR_MAX_DURATION; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +/* + * IR Tx Carrier Duty Cycle register helpers + */ +static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c, + unsigned int duty_cycle) +{ + u32 n; + n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ + if (n != 0) + n--; + if (n > 15) + n = 15; + cx25840_write4(c, CX25840_IR_CDUTY_REG, n); + return DIV_ROUND_CLOSEST((n + 1) * 100, 16); +} + +/* + * IR Filter Register helpers + */ +static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns) +{ + u32 count = ns_to_lpf_count(min_width_ns); + cx25840_write4(c, CX25840_IR_FILTR_REG, count); + return lpf_count_to_ns(count); +} + +/* + * IR IRQ Enable Register helpers + */ +static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx23885(state) || is_cx23887(state)) + mask ^= IRQEN_MSK; + mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); + cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, + ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); +} + +static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask) +{ + struct cx25840_state *state = to_state(sd); + + if (is_cx23885(state) || is_cx23887(state)) + mask ^= IRQEN_MSK; + mask &= IRQEN_TSE; + cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask); +} + +/* + * V4L2 Subdevice IR Ops + */ +int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c = NULL; + unsigned long flags; + + union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; + unsigned int i, j, k; + u32 events, v; + int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; + u32 cntrl, irqen, stats; + + *handled = false; + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + + /* Only support the IR controller for the CX2388[57] AV Core for now */ + if (!(is_cx23885(state) || is_cx23887(state))) + return -ENODEV; + + cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); + irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); + if (is_cx23885(state) || is_cx23887(state)) + irqen ^= IRQEN_MSK; + stats = cx25840_read4(c, CX25840_IR_STATS_REG); + + tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ + rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ + rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ + ror = stats & STATS_ROR; /* Rx FIFO Over Run */ + + tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ + rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ + rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ + roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ + + v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n", + tsr ? "tsr" : " ", rsr ? "rsr" : " ", + rto ? "rto" : " ", ror ? "ror" : " ", + stats & STATS_TBY ? "tby" : " ", + stats & STATS_RBY ? "rby" : " "); + + v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n", + tse ? "tse" : " ", rse ? "rse" : " ", + rte ? "rte" : " ", roe ? "roe" : " "); + + /* + * Transmitter interrupt service + */ + if (tse && tsr) { + /* + * TODO: + * Check the watermark threshold setting + * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo + * Push the data to the hardware FIFO. + * If there was nothing more to send in the tx_kfifo, disable + * the TSR IRQ and notify the v4l2_device. + * If there was something in the tx_kfifo, check the tx_kfifo + * level and notify the v4l2_device, if it is low. + */ + /* For now, inhibit TSR interrupt until Tx is implemented */ + irqenable_tx(sd, 0); + events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); + *handled = true; + } + + /* + * Receiver interrupt service + */ + kror = 0; + if ((rse && rsr) || (rte && rto)) { + /* + * Receive data on RSR to clear the STATS_RSR. + * Receive data on RTO, since we may not have yet hit the RSR + * watermark when we receive the RTO. + */ + for (i = 0, v = FIFO_RX_NDV; + (v & FIFO_RX_NDV) && !kror; i = 0) { + for (j = 0; + (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { + v = cx25840_read4(c, CX25840_IR_FIFO_REG); + rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; + i++; + } + if (i == 0) + break; + j = i * sizeof(union cx25840_ir_fifo_rec); + k = kfifo_in_locked(&ir_state->rx_kfifo, + (unsigned char *) rx_data, j, + &ir_state->rx_kfifo_lock); + if (k != j) + kror++; /* rx_kfifo over run */ + } + *handled = true; + } + + events = 0; + v = 0; + if (kror) { + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver software FIFO overrun\n"); + } + if (roe && ror) { + /* + * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear + * the Rx FIFO Over Run status (STATS_ROR) + */ + v |= CNTRL_RFE; + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); + } + if (rte && rto) { + /* + * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear + * the Rx Pulse Width Timer Time Out (STATS_RTO) + */ + v |= CNTRL_RXE; + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + } + if (v) { + /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ + cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v); + cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl); + *handled = true; + } + spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); + if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); + + if (events) + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); + return 0; +} + +/* Receiver */ +static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + bool invert; + u16 divider; + unsigned int i, n; + union cx25840_ir_fifo_rec *p; + unsigned u, v; + + if (ir_state == NULL) + return -ENODEV; + + invert = (bool) atomic_read(&ir_state->rx_invert); + divider = (u16) atomic_read(&ir_state->rxclk_divider); + + n = count / sizeof(union cx25840_ir_fifo_rec) + * sizeof(union cx25840_ir_fifo_rec); + if (n == 0) { + *num = 0; + return 0; + } + + n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n, + &ir_state->rx_kfifo_lock); + + n /= sizeof(union cx25840_ir_fifo_rec); + *num = n * sizeof(union cx25840_ir_fifo_rec); + + for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { + + if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + /* Assume RTO was because of no IR light input */ + u = 0; + v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); + } else { + u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; + if (invert) + u = u ? 0 : 1; + } + + v = (unsigned) pulse_width_count_to_ns( + (u16) (p->hw_fifo_data & FIFO_RXTX), divider); + if (v > IR_MAX_DURATION) + v = IR_MAX_DURATION; + + p->ir_core_data.pulse = u; + p->ir_core_data.duration = v; + + v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n", + v, u ? "mark" : "space"); + } + return 0; +} + +static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + mutex_lock(&ir_state->rx_params_lock); + memcpy(p, &ir_state->rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + mutex_lock(&ir_state->rx_params_lock); + + /* Disable or slow down all IR Rx circuits and counters */ + irqenable_rx(sd, 0); + control_rx_enable(c, false); + control_rx_demodulation_enable(c, false); + control_rx_s_edge_detection(c, CNTRL_EDG_NONE); + filter_rx_s_min_width(c, 0); + cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD); + + ir_state->rx_params.shutdown = true; + + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + struct v4l2_subdev_ir_parameters *o; + u16 rxclk_divider; + + if (ir_state == NULL) + return -ENODEV; + + if (p->shutdown) + return cx25840_ir_rx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + c = ir_state->c; + o = &ir_state->rx_params; + + mutex_lock(&ir_state->rx_params_lock); + + o->shutdown = p->shutdown; + + p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + o->mode = p->mode; + + p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); + o->bytes_per_data_element = p->bytes_per_data_element; + + /* Before we tweak the hardware, we have to disable the receiver */ + irqenable_rx(sd, 0); + control_rx_enable(c, false); + + control_rx_demodulation_enable(c, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq, + &rxclk_divider); + + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = 50; + o->duty_cycle = p->duty_cycle; + + control_rx_s_carrier_window(c, p->carrier_freq, + &p->carrier_range_lower, + &p->carrier_range_upper); + o->carrier_range_lower = p->carrier_range_lower; + o->carrier_range_upper = p->carrier_range_upper; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); + } else { + p->max_pulse_width = + rxclk_rx_s_max_pulse_width(c, p->max_pulse_width, + &rxclk_divider); + } + o->max_pulse_width = p->max_pulse_width; + atomic_set(&ir_state->rxclk_divider, rxclk_divider); + + p->noise_filter_min_width = + filter_rx_s_min_width(c, p->noise_filter_min_width); + o->noise_filter_min_width = p->noise_filter_min_width; + + p->resolution = clock_divider_to_resolution(rxclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_rx_irq_watermark(c, RX_FIFO_HALF_FULL); + + control_rx_s_edge_detection(c, CNTRL_EDG_BOTH); + + o->invert_level = p->invert_level; + atomic_set(&ir_state->rx_invert, p->invert_level); + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + unsigned long flags; + + spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); + kfifo_reset(&ir_state->rx_kfifo); + spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); + if (p->interrupt_enable) + irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); + control_rx_enable(c, p->enable); + } + + mutex_unlock(&ir_state->rx_params_lock); + return 0; +} + +/* Transmitter */ +static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; +#if 0 + /* + * FIXME - the code below is an incomplete and untested sketch of what + * may need to be done. The critical part is to get 4 (or 8) pulses + * from the tx_kfifo, or converted from ns to the proper units from the + * input, and push them off to the hardware Tx FIFO right away, if the + * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in + * a less critical timeframe. Also watch out for overruning the + * tx_kfifo - don't let it happen and let the caller know not all his + * pulses were written. + */ + u32 *ns_pulse = (u32 *) buf; + unsigned int n; + u32 fifo_pulse[FIFO_TX_DEPTH]; + u32 mark; + + /* Compute how much we can fit in the tx kfifo */ + n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo); + n = min(n, (unsigned int) count); + n /= sizeof(u32); + + /* FIXME - turn on Tx Fifo service interrupt + * check hardware fifo level, and other stuff + */ + for (i = 0; i < n; ) { + for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) { + mark = ns_pulse[i] & LEVEL_MASK; + fifo_pulse[j] = ns_to_pulse_width_count( + ns_pulse[i] & + ~LEVEL_MASK, + ir_state->txclk_divider); + if (mark) + fifo_pulse[j] &= FIFO_RXTX_LVL; + i++; + } + kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse, + j * sizeof(u32)); + } + *num = n * sizeof(u32); +#else + /* For now enable the Tx FIFO Service interrupt & pretend we did work */ + irqenable_tx(sd, IRQEN_TSE); + *num = count; +#endif + return 0; +} + +static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + mutex_lock(&ir_state->tx_params_lock); + memcpy(p, &ir_state->tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + +static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + + if (ir_state == NULL) + return -ENODEV; + + c = ir_state->c; + mutex_lock(&ir_state->tx_params_lock); + + /* Disable or slow down all IR Tx circuits and counters */ + irqenable_tx(sd, 0); + control_tx_enable(c, false); + control_tx_modulation_enable(c, false); + cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD); + + ir_state->tx_params.shutdown = true; + + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + +static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx25840_ir_state *ir_state = to_ir_state(sd); + struct i2c_client *c; + struct v4l2_subdev_ir_parameters *o; + u16 txclk_divider; + + if (ir_state == NULL) + return -ENODEV; + + if (p->shutdown) + return cx25840_ir_tx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + c = ir_state->c; + o = &ir_state->tx_params; + mutex_lock(&ir_state->tx_params_lock); + + o->shutdown = p->shutdown; + + p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + o->mode = p->mode; + + p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); + o->bytes_per_data_element = p->bytes_per_data_element; + + /* Before we tweak the hardware, we have to disable the transmitter */ + irqenable_tx(sd, 0); + control_tx_enable(c, false); + + control_tx_modulation_enable(c, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq, + &txclk_divider); + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle); + o->duty_cycle = p->duty_cycle; + + p->max_pulse_width = + (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); + } else { + p->max_pulse_width = + txclk_tx_s_max_pulse_width(c, p->max_pulse_width, + &txclk_divider); + } + o->max_pulse_width = p->max_pulse_width; + atomic_set(&ir_state->txclk_divider, txclk_divider); + + p->resolution = clock_divider_to_resolution(txclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY); + + control_tx_polarity_invert(c, p->invert_carrier_sense); + o->invert_carrier_sense = p->invert_carrier_sense; + + /* + * FIXME: we don't have hardware help for IO pin level inversion + * here like we have on the CX23888. + * Act on this with some mix of logical inversion of data levels, + * carrier polarity, and carrier duty cycle. + */ + o->invert_level = p->invert_level; + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + /* reset tx_fifo here */ + if (p->interrupt_enable) + irqenable_tx(sd, IRQEN_TSE); + control_tx_enable(c, p->enable); + } + + mutex_unlock(&ir_state->tx_params_lock); + return 0; +} + + +/* + * V4L2 Subdevice Core Ops support + */ +int cx25840_ir_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *c = state->c; + char *s; + int i, j; + u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr; + + /* The CX23888 chip doesn't have an IR controller on the A/V core */ + if (is_cx23888(state)) + return 0; + + cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); + txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD; + rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD; + cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC; + stats = cx25840_read4(c, CX25840_IR_STATS_REG); + irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); + if (is_cx23885(state) || is_cx23887(state)) + irqen ^= IRQEN_MSK; + filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF; + + v4l2_info(sd, "IR Receiver:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_RXE ? "yes" : "no"); + v4l2_info(sd, "\tDemodulation from a carrier: %s\n", + cntrl & CNTRL_DMD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_RFE ? "enabled" : "disabled"); + switch (cntrl & CNTRL_EDG) { + case CNTRL_EDG_NONE: + s = "disabled"; + break; + case CNTRL_EDG_FALL: + s = "falling edge"; + break; + case CNTRL_EDG_RISE: + s = "rising edge"; + break; + case CNTRL_EDG_BOTH: + s = "rising & falling edges"; + break; + default: + s = "??? edge"; + break; + } + v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); + v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", + cntrl & CNTRL_R ? "not loaded" : "overflow marker"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); + v4l2_info(sd, "\tLoopback mode: %s\n", + cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); + if (cntrl & CNTRL_DMD) { + v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(rxclk)); + switch (cntrl & CNTRL_WIN) { + case CNTRL_WIN_3_3: + i = 3; + j = 3; + break; + case CNTRL_WIN_4_3: + i = 4; + j = 3; + break; + case CNTRL_WIN_3_4: + i = 3; + j = 4; + break; + case CNTRL_WIN_4_4: + i = 4; + j = 4; + break; + default: + i = 0; + j = 0; + break; + } + v4l2_info(sd, "\tNext carrier edge window: 16 clocks " + "-%1d/+%1d, %u to %u Hz\n", i, j, + clock_divider_to_freq(rxclk, 16 + j), + clock_divider_to_freq(rxclk, 16 - i)); + } + v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); + v4l2_info(sd, "\tLow pass filter: %s\n", + filtr ? "enabled" : "disabled"); + if (filtr) + v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " + "%u ns\n", + lpf_count_to_us(filtr), + lpf_count_to_ns(filtr)); + v4l2_info(sd, "\tPulse width timer timed-out: %s\n", + stats & STATS_RTO ? "yes" : "no"); + v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", + irqen & IRQEN_RTE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO overrun: %s\n", + stats & STATS_ROR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", + irqen & IRQEN_ROE ? "enabled" : "disabled"); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_RBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_RSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_RSE ? "enabled" : "disabled"); + + v4l2_info(sd, "IR Transmitter:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_TXE ? "yes" : "no"); + v4l2_info(sd, "\tModulation onto a carrier: %s\n", + cntrl & CNTRL_MOD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_TFE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_TIC ? "not empty" : "half full or less"); + v4l2_info(sd, "\tCarrier polarity: %s\n", + cntrl & CNTRL_CPL ? "space:burst mark:noburst" + : "space:noburst mark:burst"); + if (cntrl & CNTRL_MOD) { + v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(txclk)); + v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", + cduty + 1); + } + v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_TBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_TSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_TSE ? "enabled" : "disabled"); + + return 0; +} + + +const struct v4l2_subdev_ir_ops cx25840_ir_ops = { + .rx_read = cx25840_ir_rx_read, + .rx_g_parameters = cx25840_ir_rx_g_parameters, + .rx_s_parameters = cx25840_ir_rx_s_parameters, + + .tx_write = cx25840_ir_tx_write, + .tx_g_parameters = cx25840_ir_tx_g_parameters, + .tx_s_parameters = cx25840_ir_tx_s_parameters, +}; + + +static const struct v4l2_subdev_ir_parameters default_rx_params = { + .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */ + + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + .noise_filter_min_width = 333333, /* ns */ + .carrier_range_lower = 35000, + .carrier_range_upper = 37000, + .invert_level = false, +}; + +static const struct v4l2_subdev_ir_parameters default_tx_params = { + .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ + .duty_cycle = 25, /* 25 % - RC-5 carrier */ + .invert_level = false, + .invert_carrier_sense = false, +}; + +int cx25840_ir_probe(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state; + struct v4l2_subdev_ir_parameters default_params; + + /* Only init the IR controller for the CX2388[57] AV Core for now */ + if (!(is_cx23885(state) || is_cx23887(state))) + return 0; + + ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL); + if (ir_state == NULL) + return -ENOMEM; + + spin_lock_init(&ir_state->rx_kfifo_lock); + if (kfifo_alloc(&ir_state->rx_kfifo, + CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) { + kfree(ir_state); + return -ENOMEM; + } + + ir_state->c = state->c; + state->ir_state = ir_state; + + /* Ensure no interrupts arrive yet */ + if (is_cx23885(state) || is_cx23887(state)) + cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK); + else + cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); + + mutex_init(&ir_state->rx_params_lock); + memcpy(&default_params, &default_rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); + + mutex_init(&ir_state->tx_params_lock); + memcpy(&default_params, &default_tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); + + return 0; +} + +int cx25840_ir_remove(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct cx25840_ir_state *ir_state = to_ir_state(sd); + + if (ir_state == NULL) + return -ENODEV; + + cx25840_ir_rx_shutdown(sd); + cx25840_ir_tx_shutdown(sd); + + kfifo_free(&ir_state->rx_kfifo); + kfree(ir_state); + state->ir_state = NULL; + return 0; +} diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index c7e5851d348..99dbae11759 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -6,7 +6,7 @@ config VIDEO_CX88 select VIDEOBUF_DMA_SG select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_IR + depends on VIDEO_IR select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Conexant 2388x based diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 33082c96745..4f383cdf529 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -54,6 +54,12 @@ Data type declarations - Can be moded to a header file later ****************************************************************************/ +struct cx88_audio_buffer { + unsigned int bpl; + struct btcx_riscmem risc; + struct videobuf_dmabuf dma; +}; + struct cx88_audio_dev { struct cx88_core *core; struct cx88_dmaqueue q; @@ -75,7 +81,7 @@ struct cx88_audio_dev { struct videobuf_dmabuf *dma_risc; - struct cx88_buffer *buf; + struct cx88_audio_buffer *buf; struct snd_pcm_substream *substream; }; @@ -123,7 +129,7 @@ MODULE_PARM_DESC(debug,"enable debug messages"); static int _cx88_start_audio_dma(snd_cx88_card_t *chip) { - struct cx88_buffer *buf = chip->buf; + struct cx88_audio_buffer *buf = chip->buf; struct cx88_core *core=chip->core; struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25]; @@ -283,7 +289,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip) BUG_ON(!chip->dma_size); dprintk(2,"Freeing buffer\n"); - videobuf_sg_dma_unmap(&chip->pci->dev, chip->dma_risc); + videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); videobuf_dma_free(chip->dma_risc); btcx_riscmem_free(chip->pci,&chip->buf->risc); kfree(chip->buf); @@ -376,7 +382,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); struct videobuf_dmabuf *dma; - struct cx88_buffer *buf; + struct cx88_audio_buffer *buf; int ret; if (substream->runtime->dma_area) { @@ -391,30 +397,25 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, BUG_ON(!chip->dma_size); BUG_ON(chip->num_periods & (chip->num_periods-1)); - buf = videobuf_sg_alloc(sizeof(*buf)); + buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (NULL == buf) return -ENOMEM; - buf->vb.memory = V4L2_MEMORY_MMAP; - buf->vb.field = V4L2_FIELD_NONE; - buf->vb.width = chip->period_size; - buf->bpl = chip->period_size; - buf->vb.height = chip->num_periods; - buf->vb.size = chip->dma_size; + buf->bpl = chip->period_size; - dma = videobuf_to_dma(&buf->vb); + dma = &buf->dma; videobuf_dma_init(dma); ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, - (PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT)); + (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_sg_dma_map(&chip->pci->dev, dma); + ret = videobuf_dma_map(&chip->pci->dev, dma); if (ret < 0) goto error; ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist, - buf->vb.width, buf->vb.height, 1); + chip->period_size, chip->num_periods, 1); if (ret < 0) goto error; @@ -422,12 +423,10 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - buf->vb.state = VIDEOBUF_PREPARED; - chip->buf = buf; chip->dma_risc = dma; - substream->runtime->dma_area = chip->dma_risc->vmalloc; + substream->runtime->dma_area = chip->dma_risc->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; return 0; @@ -740,7 +739,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, pci_set_master(pci); - chip = (snd_cx88_card_t *) card->private_data; + chip = card->private_data; core = cx88_core_get(pci); if (NULL == core) { diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 2918a6e38fe..e8416b76da6 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -45,6 +45,10 @@ static unsigned int latency = UNSET; module_param(latency,int,0444); MODULE_PARM_DESC(latency,"pci latency timer"); +static int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(latency, "Disable IR support"); + #define info_printk(core, fmt, arg...) \ printk(KERN_INFO "%s: " fmt, core->name , ## arg) @@ -3498,7 +3502,10 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) } cx88_card_setup(core); - cx88_ir_init(core, pci); + if (!disable_ir) { + cx88_i2c_init_ir(core); + cx88_ir_init(core, pci); + } return core; } diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 8b21457111b..85eb266fb35 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -218,7 +218,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index fb39f118455..82db555b22d 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -181,6 +181,11 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) } else printk("%s: i2c register FAILED\n", core->name); + return core->i2c_rc; +} + +void cx88_i2c_init_ir(struct cx88_core *core) +{ /* Instantiate the IR receiver device, if present */ if (0 == core->i2c_rc) { struct i2c_board_info info; @@ -188,26 +193,14 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) 0x18, 0x6b, 0x71, I2C_CLIENT_END }; - const unsigned short *addrp; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - /* - * We can't call i2c_new_probed_device() because it uses - * quick writes for probing and at least some R receiver - * devices only reply to reads. - */ - for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) { - if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0, - I2C_SMBUS_READ, 0, - I2C_SMBUS_QUICK, NULL) >= 0) { - info.addr = *addrp; - i2c_new_device(&core->i2c_adap, &info); - break; - } - } + /* Use quick read command for probe, some IR chips don't + * support writes */ + i2c_new_probed_device(&core->i2c_adap, &info, addr_list, + i2c_probe_func_quick_read); } - return core->i2c_rc; } /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index e185289e446..eccc5e49a35 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -30,6 +30,7 @@ #include <linux/module.h> #include "cx88.h" +#include <media/ir-core.h> #include <media/ir-common.h> #define MODULE_NAME "cx88xx" @@ -39,8 +40,8 @@ struct cx88_IR { struct cx88_core *core; struct input_dev *input; - struct ir_input_state ir; struct ir_dev_props props; + u64 ir_type; int users; @@ -51,7 +52,6 @@ struct cx88_IR { u32 sampling; u32 samples[16]; int scount; - unsigned long release; /* poll external decoder */ int polling; @@ -125,29 +125,21 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - ir_input_keydown(ir->input, &ir->ir, data); - ir_input_nokey(ir->input, &ir->ir); + ir_keydown(ir->input, data, 0); } else if (ir->mask_keydown) { /* bit set on keydown */ - if (gpio & ir->mask_keydown) { - ir_input_keydown(ir->input, &ir->ir, data); - } else { - ir_input_nokey(ir->input, &ir->ir); - } + if (gpio & ir->mask_keydown) + ir_keydown(ir->input, data, 0); } else if (ir->mask_keyup) { /* bit cleared on keydown */ - if (0 == (gpio & ir->mask_keyup)) { - ir_input_keydown(ir->input, &ir->ir, data); - } else { - ir_input_nokey(ir->input, &ir->ir); - } + if (0 == (gpio & ir->mask_keyup)) + ir_keydown(ir->input, data, 0); } else { /* can't distinguish keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data); - ir_input_nokey(ir->input, &ir->ir); + ir_keydown(ir->input, data, 0); } } @@ -439,9 +431,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); - err = ir_input_init(input_dev, &ir->ir, ir_type); - if (err < 0) - goto err_out_free; + ir->ir_type = ir_type; input_dev->name = ir->name; input_dev->phys = ir->phys; @@ -516,8 +506,6 @@ void cx88_ir_irq(struct cx88_core *core) } if (!ir->scount) { /* nothing to sample */ - if (ir->ir.keypressed && time_after(jiffies, ir->release)) - ir_input_nokey(ir->input, &ir->ir); return; } @@ -553,7 +541,7 @@ void cx88_ir_irq(struct cx88_core *core) if (ircode == 0) { /* key still pressed */ ir_dprintk("pulse distance decoded repeat code\n"); - ir->release = jiffies + msecs_to_jiffies(120); + ir_repeat(ir->input); break; } @@ -567,10 +555,8 @@ void cx88_ir_irq(struct cx88_core *core) break; } - ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f); - - ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f); - ir->release = jiffies + msecs_to_jiffies(120); + ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0xff); + ir_keydown(ir->input, (ircode >> 16) & 0xff, 0); break; case CX88_BOARD_HAUPPAUGE: case CX88_BOARD_HAUPPAUGE_DVB_T1: @@ -606,16 +592,16 @@ void cx88_ir_irq(struct cx88_core *core) if ( dev != 0x1e && dev != 0x1f ) /* not a hauppauge remote */ break; - ir_input_keydown(ir->input, &ir->ir, code); - ir->release = jiffies + msecs_to_jiffies(120); + ir_keydown(ir->input, code, toggle); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) break; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f); - ir->release = jiffies + msecs_to_jiffies(120); + /* Note: bit 0x800 being the toggle is assumed, not checked + with real hardware */ + ir_keydown(ir->input, ircode & 0x3f, ircode & 0x0800 ? 1 : 0); break; } diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index bdb03d33653..33d161a1172 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -636,6 +636,7 @@ extern struct videobuf_queue_ops cx8800_vbi_qops; /* cx88-i2c.c */ extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); +extern void cx88_i2c_init_ir(struct cx88_core *core); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index 0f505086774..5b176bd7afd 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -706,16 +706,11 @@ static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg switch (cmd) { case IOCTL_DAB_BULK: - pbulk = kmalloc(sizeof (bulk_transfer_t), GFP_KERNEL); + pbulk = memdup_user((void __user *)arg, + sizeof(bulk_transfer_t)); - if (!pbulk) { - ret = -ENOMEM; - break; - } - - if (copy_from_user (pbulk, (void __user *) arg, sizeof (bulk_transfer_t))) { - ret = -EFAULT; - kfree (pbulk); + if (IS_ERR(pbulk)) { + ret = PTR_ERR(pbulk); break; } diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig new file mode 100644 index 00000000000..6b195403564 --- /dev/null +++ b/drivers/media/video/davinci/Kconfig @@ -0,0 +1,93 @@ +config DISPLAY_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Display" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + select VIDEO_ADV7343 + select VIDEO_THS7303 + help + Support for DM6467 based display device. + + To compile this driver as a module, choose M here: the + module will be called vpif_display. + +config CAPTURE_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Capture" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + help + Support for DM6467 based capture device. + + To compile this driver as a module, choose M here: the + module will be called vpif_capture. + +config VIDEO_DAVINCI_VPIF + tristate "DaVinci VPIF Driver" + depends on DISPLAY_DAVINCI_DM646X_EVM + help + Support for DaVinci VPIF Driver. + + To compile this driver as a module, choose M here: the + module will be called vpif. + +config VIDEO_VPSS_SYSTEM + tristate "VPSS System module driver" + depends on ARCH_DAVINCI + help + Support for vpss system module for video driver + +config VIDEO_VPFE_CAPTURE + tristate "VPFE Video Capture Driver" + depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) + select VIDEOBUF_DMA_CONTIG + help + Support for DMx/AMx VPFE based frame grabber. This is the + common V4L2 module for following DMx/AMx SoCs from Texas + Instruments:- DM6446, DM365, DM355 & AM3517/05. + + To compile this driver as a module, choose M here: the + module will be called vpfe-capture. + +config VIDEO_DM6446_CCDC + tristate "DM6446 CCDC HW module" + depends on VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_DM355_CCDC + tristate "DM355 CCDC HW module" + depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_ISIF + tristate "ISIF HW module" + depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables ISIF hw module. This is the hardware module for + configuring ISIF in VPFE to capture Raw Bayer RGB data from + a image sensor or YUV data from a YUV source. + + To compile this driver as a module, choose M here: the + module will be called vpfe. diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index c7be0e09782..66aefd6eef5 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_EM28XX depends on VIDEO_DEV && I2C && INPUT select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_IR + depends on VIDEO_IR select VIDEOBUF_VMALLOC select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3a4fd851451..e7efb4bffab 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -158,6 +158,22 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { { -1, -1, -1, -1}, }; +/* + * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map: + * EM_GPIO_0 - currently unknown + * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on) + * EM_GPIO_2 - currently unknown + * EM_GPIO_3 - currently unknown + * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset) + * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset) + * EM_GPIO_6 - currently unknown + * EM_GPIO_7 - currently unknown + */ +static struct em28xx_reg_seq kworld_a340_digital[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + /* Pinnacle Hybrid Pro eb1a:2881 */ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, @@ -1667,6 +1683,16 @@ struct em28xx_board em28xx_boards[] = { .tuner_gpio = reddo_dvb_c_usb_box, .has_dvb = 1, }, + /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold + * initially as the KWorld PlusTV 340U, then as the UB435-Q. + * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */ + [EM2870_BOARD_KWORLD_A340] = { + .name = "KWorld PlusTV 340U or UB435-Q (ATSC)", + .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ + .has_dvb = 1, + .dvb_gpio = kworld_a340_digital, + .tuner_gpio = default_tuner_gpio, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1788,6 +1814,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, { USB_DEVICE(0xeb1a, 0x50a6), .driver_info = EM2860_BOARD_GADMEI_UTV330 }, + { USB_DEVICE(0x1b80, 0xa340), + .driver_info = EM2870_BOARD_KWORLD_A340 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -2357,7 +2385,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (dev->init_data.name) info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); } void em28xx_card_setup(struct em28xx *dev) diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cf1d8c3655f..3ac8d3025fe 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -30,11 +30,13 @@ #include "tuner-simple.h" #include "lgdt330x.h" +#include "lgdt3305.h" #include "zl10353.h" #include "s5h1409.h" #include "mt352.h" #include "mt352_priv.h" /* FIXME */ #include "tda1002x.h" +#include "tda18271.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -231,6 +233,18 @@ static struct lgdt330x_config em2880_lgdt3303_dev = { .demod_chip = LGDT3303, }; +static struct lgdt3305_config em2870_lgdt3304_dev = { + .i2c_addr = 0x0e, + .demod_chip = LGDT3304, + .spectral_inversion = 1, + .deny_i2c_rptr = 1, + .mpeg_mode = LGDT3305_MPEG_PARALLEL, + .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .vsb_if_khz = 3250, + .qam_if_khz = 4000, +}; + static struct zl10353_config em28xx_zl10353_with_xc3028 = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -247,6 +261,17 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK }; +static struct tda18271_std_map kworld_a340_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0, + .if_lvl = 1, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config kworld_a340_config = { + .std_map = &kworld_a340_std_map, +}; + static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -572,6 +597,14 @@ static int dvb_init(struct em28xx *dev) } } break; + case EM2870_BOARD_KWORLD_A340: + dvb->frontend = dvb_attach(lgdt3305_attach, + &em2870_lgdt3304_dev, + &dev->i2c_adap); + if (dvb->frontend != NULL) + dvb_attach(tda18271_attach, dvb->frontend, 0x60, + &dev->i2c_adap, &kworld_a340_config); + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 5c3fd9411b1..6759cd5570d 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -65,17 +65,14 @@ struct em28xx_ir_poll_result { struct em28xx_IR { struct em28xx *dev; struct input_dev *input; - struct ir_input_state ir; char name[32]; char phys[32]; /* poll external decoder */ int polling; struct delayed_work work; - unsigned int last_toggle:1; unsigned int full_code:1; unsigned int last_readcount; - unsigned int repeat_interval; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); @@ -291,67 +288,39 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static void em28xx_ir_handle_key(struct em28xx_IR *ir) { int result; - int do_sendkey = 0; struct em28xx_ir_poll_result poll_result; /* read the registers containing the IR status */ result = ir->get_key(ir, &poll_result); - if (result < 0) { + if (unlikely(result < 0)) { dprintk("ir->get_key() failed %d\n", result); return; } - dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n", - poll_result.toggle_bit, poll_result.read_count, - ir->last_readcount, poll_result.rc_address, - poll_result.rc_data[0]); - - if (ir->dev->chip_id == CHIP_ID_EM2874) { - /* The em2874 clears the readcount field every time the - register is read. The em2860/2880 datasheet says that it - is supposed to clear the readcount, but it doesn't. So with - the em2874, we are looking for a non-zero read count as - opposed to a readcount that is incrementing */ - ir->last_readcount = 0; - } - - if (poll_result.read_count == 0) { - /* The button has not been pressed since the last read */ - } else if (ir->last_toggle != poll_result.toggle_bit) { - /* A button has been pressed */ - dprintk("button has been pressed\n"); - ir->last_toggle = poll_result.toggle_bit; - ir->repeat_interval = 0; - do_sendkey = 1; - } else if (poll_result.toggle_bit == ir->last_toggle && - poll_result.read_count > 0 && - poll_result.read_count != ir->last_readcount) { - /* The button is still being held down */ - dprintk("button being held down\n"); - - /* Debouncer for first keypress */ - if (ir->repeat_interval++ > 9) { - /* Start repeating after 1 second */ - do_sendkey = 1; - } - } - - if (do_sendkey) { - dprintk("sending keypress\n"); - + if (unlikely(poll_result.read_count != ir->last_readcount)) { + dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + poll_result.toggle_bit, poll_result.read_count, + poll_result.rc_address, poll_result.rc_data[0]); if (ir->full_code) - ir_input_keydown(ir->input, &ir->ir, - poll_result.rc_address << 8 | - poll_result.rc_data[0]); + ir_keydown(ir->input, + poll_result.rc_address << 8 | + poll_result.rc_data[0], + poll_result.toggle_bit); else - ir_input_keydown(ir->input, &ir->ir, - poll_result.rc_data[0]); - - ir_input_nokey(ir->input, &ir->ir); + ir_keydown(ir->input, + poll_result.rc_data[0], + poll_result.toggle_bit); + + if (ir->dev->chip_id == CHIP_ID_EM2874) + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + else + ir->last_readcount = poll_result.read_count; } - - ir->last_readcount = poll_result.read_count; - return; } static void em28xx_ir_work(struct work_struct *work) @@ -466,11 +435,6 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - /* Set IR protocol */ - err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); - if (err < 0) - goto err_out_free; - input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 20090e34173..7b9ec6e493e 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -654,12 +654,12 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) } if (buf != NULL && dev->capture_type == 2) { - if (len > 4 && p[0] == 0x88 && p[1] == 0x88 && + if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && p[2] == 0x88 && p[3] == 0x88) { p += 4; len -= 4; } - if (len > 4 && p[0] == 0x22 && p[1] == 0x5a) { + if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { em28xx_isocdbg("Video frame %d, len=%i, %s\n", p[2], len, (p[2] & 1) ? "odd" : "even"); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index b252d1b1b2a..1c61a6b65d2 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -32,6 +32,7 @@ #include <linux/i2c.h> #include <linux/mutex.h> #include <media/ir-kbd-i2c.h> +#include <media/ir-core.h> #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) #include <media/videobuf-dvb.h> #endif @@ -113,6 +114,7 @@ #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 #define EM2800_BOARD_VC211A 74 #define EM2882_BOARD_DIKOM_DK300 75 +#define EM2870_BOARD_KWORLD_A340 76 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c new file mode 100644 index 00000000000..43d208f1f58 --- /dev/null +++ b/drivers/media/video/fsl-viu.c @@ -0,0 +1,1632 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Freescale VIU video driver + * + * Authors: Hongjun Chen <hong-jun.chen@freescale.com> + * Porting to 2.6.35 by DENX Software Engineering, + * Anatolij Gustschin <agust@denx.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf-dma-contig.h> + +#define DRV_NAME "fsl_viu" +#define VIU_MAJOR_VERSION 0 +#define VIU_MINOR_VERSION 5 +#define VIU_RELEASE 0 +#define VIU_VERSION KERNEL_VERSION(VIU_MAJOR_VERSION, \ + VIU_MINOR_VERSION, \ + VIU_RELEASE) + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ + +#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ + +/* I2C address of video decoder chip is 0x4A */ +#define VIU_VIDEO_DECODER_ADDR 0x25 + +/* supported controls */ +static struct v4l2_queryctrl viu_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 0x10, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 127, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0, + .flags = 0, + } +}; + +static int qctl_regs[ARRAY_SIZE(viu_qctrl)]; + +static int info_level; + +#define dprintk(level, fmt, arg...) \ + do { \ + if (level <= info_level) \ + printk(KERN_DEBUG "viu: " fmt , ## arg); \ + } while (0) + +/* + * Basic structures + */ +struct viu_fmt { + char name[32]; + u32 fourcc; /* v4l2 format id */ + u32 pixelformat; + int depth; +}; + +static struct viu_fmt formats[] = { + { + .name = "RGB-16 (5/B-6/G-5/R)", + .fourcc = V4L2_PIX_FMT_RGB565, + .pixelformat = V4L2_PIX_FMT_RGB565, + .depth = 16, + }, { + .name = "RGB-32 (A-R-G-B)", + .fourcc = V4L2_PIX_FMT_RGB32, + .pixelformat = V4L2_PIX_FMT_RGB32, + .depth = 32, + } +}; + +struct viu_dev; +struct viu_buf; + +/* buffer for one video frame */ +struct viu_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + struct viu_fmt *fmt; +}; + +struct viu_dmaqueue { + struct viu_dev *dev; + struct list_head active; + struct list_head queued; + struct timer_list timeout; +}; + +struct viu_status { + u32 field_irq; + u32 vsync_irq; + u32 hsync_irq; + u32 vstart_irq; + u32 dma_end_irq; + u32 error_irq; +}; + +struct viu_reg { + u32 status_cfg; + u32 luminance; + u32 chroma_r; + u32 chroma_g; + u32 chroma_b; + u32 field_base_addr; + u32 dma_inc; + u32 picture_count; + u32 req_alarm; + u32 alpha; +} __attribute__ ((packed)); + +struct viu_dev { + struct v4l2_device v4l2_dev; + struct mutex lock; + spinlock_t slock; + int users; + + struct device *dev; + /* various device info */ + struct video_device *vdev; + struct viu_dmaqueue vidq; + enum v4l2_field capfield; + int field; + int first; + int dma_done; + + /* Hardware register area */ + struct viu_reg *vr; + + /* Interrupt vector */ + int irq; + struct viu_status irqs; + + /* video overlay */ + struct v4l2_framebuffer ovbuf; + struct viu_fmt *ovfmt; + unsigned int ovenable; + enum v4l2_field ovfield; + + /* crop */ + struct v4l2_rect crop_current; + + /* clock pointer */ + struct clk *clk; + + /* decoder */ + struct v4l2_subdev *decoder; +}; + +struct viu_fh { + struct viu_dev *dev; + + /* video capture */ + struct videobuf_queue vb_vidq; + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip clips[1]; + + /* video capture */ + struct viu_fmt *fmt; + int width, height, sizeimage; + enum v4l2_buf_type type; +}; + +static struct viu_reg reg_val; + +/* + * Macro definitions of VIU registers + */ + +/* STATUS_CONFIG register */ +enum status_config { + SOFT_RST = 1 << 0, + + ERR_MASK = 0x0f << 4, /* Error code mask */ + ERR_NO = 0x00, /* No error */ + ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */ + ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */ + ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */ + ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */ + ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */ + ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */ + ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */ + ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */ + ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */ + ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */ + + INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */ + INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */ + INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */ + INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */ + INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */ + INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */ + INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */ + + INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */ + INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */ + INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */ + INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */ + INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */ + INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */ + + DMA_ACT = 0x01 << 27, /* Enable DMA transfer */ + FIELD_NO = 0x01 << 28, /* Field number */ + DITHER_ON = 0x01 << 29, /* Dithering is on */ + ROUND_ON = 0x01 << 30, /* Round is on */ + MODE_32BIT = 0x01 << 31, /* Data in RGBa888, + * 0 in RGB565 + */ +}; + +#define norm_maxw() 720 +#define norm_maxh() 576 + +#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \ + INT_HSYNC_STATUS | INT_VSTART_STATUS | \ + INT_DMA_END_STATUS | INT_ERROR_STATUS) + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static irqreturn_t viu_intr(int irq, void *dev_id); + +struct viu_fmt *format_by_fourcc(int fourcc) +{ + int i; + + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == fourcc) + return formats + i; + } + + dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + return NULL; +} + +void viu_start_dma(struct viu_dev *dev) +{ + struct viu_reg *vr = dev->vr; + + dev->field = 0; + + /* Enable DMA operation */ + out_be32(&vr->status_cfg, SOFT_RST); + out_be32(&vr->status_cfg, INT_FIELD_EN); +} + +void viu_stop_dma(struct viu_dev *dev) +{ + struct viu_reg *vr = dev->vr; + int cnt = 100; + u32 status_cfg; + + out_be32(&vr->status_cfg, 0); + + /* Clear pending interrupts */ + status_cfg = in_be32(&vr->status_cfg); + if (status_cfg & 0x3f0000) + out_be32(&vr->status_cfg, status_cfg & 0x3f0000); + + if (status_cfg & DMA_ACT) { + do { + status_cfg = in_be32(&vr->status_cfg); + if (status_cfg & INT_DMA_END_STATUS) + break; + } while (cnt--); + + if (cnt < 0) { + /* timed out, issue soft reset */ + out_be32(&vr->status_cfg, SOFT_RST); + out_be32(&vr->status_cfg, 0); + } else { + /* clear DMA_END and other pending irqs */ + out_be32(&vr->status_cfg, status_cfg & 0x3f0000); + } + } + + dev->field = 0; +} + +static int restart_video_queue(struct viu_dmaqueue *vidq) +{ + struct viu_buf *buf, *prev; + + dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq); + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + dprintk(2, "restart_queue [%p/%d]: restart dma\n", + buf, buf->vb.i); + + viu_stop_dma(vidq->dev); + + /* cancel all outstanding capture requests */ + list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) { + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&vidq->queued)) + return 0; + buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); + if (prev == NULL) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + + dprintk(1, "Restarting video dma\n"); + viu_stop_dma(vidq->dev); + viu_start_dma(vidq->dev); + + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] restart_queue - first active\n", + buf, buf->vb.i); + + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] restart_queue - move to active\n", + buf, buf->vb.i); + } else { + return 0; + } + prev = buf; + } +} + +static void viu_vid_timeout(unsigned long data) +{ + struct viu_dev *dev = (struct viu_dev *)data; + struct viu_buf *buf; + struct viu_dmaqueue *vidq = &dev->vidq; + + while (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i); + } + + restart_video_queue(vidq); +} + +/* + * Videobuf operations + */ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct viu_fh *fh = vq->priv_data; + + *size = fh->width * fh->height * fh->fmt->depth >> 3; + if (*count == 0) + *count = 32; + + while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024) + (*count)--; + + dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size); + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) +{ + struct videobuf_buffer *vb = &buf->vb; + void *vaddr = NULL; + + BUG_ON(in_interrupt()); + + videobuf_waiton(&buf->vb, 0, 0); + + if (vq->int_ops && vq->int_ops->vaddr) + vaddr = vq->int_ops->vaddr(vb); + + if (vaddr) + videobuf_dma_contig_free(vq, &buf->vb); + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) +{ + struct viu_reg *vr = dev->vr; + int bpp; + + /* setup the DMA base address */ + reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb); + + dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n", + buf, buf->vb.i, (unsigned long)reg_val.field_base_addr); + + /* interlace is on by default, set horizontal DMA increment */ + reg_val.status_cfg = 0; + bpp = buf->fmt->depth >> 3; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 4; + break; + default: + dprintk(0, "doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + /* setup picture_count register */ + reg_val.picture_count = (buf->vb.height / 2) << 16 | + buf->vb.width; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + buf->vb.state = VIDEOBUF_ACTIVE; + dev->capfield = buf->vb.field; + + /* reset dma increment if needed */ + if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) + reg_val.dma_inc = 0; + + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->picture_count, reg_val.picture_count); + out_be32(&vr->field_base_addr, reg_val.field_base_addr); + mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct viu_fh *fh = vq->priv_data; + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + int rc; + + BUG_ON(fh->fmt == NULL); + + if (fh->width < 48 || fh->width > norm_maxw() || + fh->height < 32 || fh->height > norm_maxh()) + return -EINVAL; + buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + } + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->fmt = fh->fmt; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = fh->dev; + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_buf *prev; + + if (!list_empty(&vidq->queued)) { + dprintk(1, "adding vb queue=0x%08lx\n", + (unsigned long)&buf->vb.queue); + dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", + vidq, &vidq->queued); + dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", + dev, &vidq->queued, vidq->queued.next, + vidq->queued.prev); + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", + buf, buf->vb.i); + } else if (list_empty(&vidq->active)) { + dprintk(1, "adding vb active=0x%08lx\n", + (unsigned long)&buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] buffer_queue - first active\n", + buf, buf->vb.i); + + buffer_activate(dev, buf); + } else { + dprintk(1, "adding vb queue2=0x%08lx\n", + (unsigned long)&buf->vb.queue); + prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); + if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] buffer_queue - append to active\n", + buf, buf->vb.i); + } else { + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", + buf, buf->vb.i); + } + } +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + + viu_stop_dma(dev); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops viu_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* + * IOCTL vidioc handling + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "viu"); + strcpy(cap->card, "viu"); + cap->version = VIU_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (f->index > NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[index].name, sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->pixelformat; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = fh->sizeimage; + return 0; +} + +static int vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fmt *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + dprintk(1, "Fourcc format (0x%08x) invalid.", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { + field = V4L2_FIELD_INTERLACED; + } else if (field != V4L2_FIELD_INTERLACED) { + dprintk(1, "Field type invalid.\n"); + return -EINVAL; + } + + maxw = norm_maxw(); + maxh = norm_maxh(); + + f->fmt.pix.field = field; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + int ret; + + ret = vidioc_try_fmt_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->sizeimage = f->fmt.pix.sizeimage; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name); + return 0; +} + +static int vidioc_g_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.win = fh->win; + return 0; +} + +static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) +{ + enum v4l2_field field; + int maxw, maxh; + + if (dev->ovbuf.base == NULL) + return -EINVAL; + if (dev->ovfmt == NULL) + return -EINVAL; + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + + field = win->field; + maxw = dev->crop_current.width; + maxh = dev->crop_current.height; + + if (field == V4L2_FIELD_ANY) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + return 0; +} + +inline void viu_activate_overlay(struct viu_reg *viu_reg) +{ + struct viu_reg *vr = viu_reg; + + out_be32(&vr->field_base_addr, reg_val.field_base_addr); + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->picture_count, reg_val.picture_count); +} + +static int viu_start_preview(struct viu_dev *dev, struct viu_fh *fh) +{ + int bpp; + + dprintk(1, "%s %dx%d %s\n", __func__, + fh->win.w.width, fh->win.w.height, dev->ovfmt->name); + + reg_val.status_cfg = 0; + + /* setup window */ + reg_val.picture_count = (fh->win.w.height / 2) << 16 | + fh->win.w.width; + + /* setup color depth and dma increment */ + bpp = dev->ovfmt->depth / 8; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 4; + break; + default: + dprintk(0, "device doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + dev->ovfield = fh->win.field; + if (!V4L2_FIELD_HAS_BOTH(dev->ovfield)) + reg_val.dma_inc = 0; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + /* setup the base address of the overlay buffer */ + reg_val.field_base_addr = (u32)dev->ovbuf.base; + + dev->ovenable = 1; + viu_activate_overlay(dev->vr); + + /* start dma */ + viu_start_dma(dev); + return 0; +} + +static int vidioc_s_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + int err; + + err = verify_preview(dev, &f->fmt.win); + if (err) + return err; + + mutex_lock(&dev->lock); + fh->win = f->fmt.win; + + spin_lock_irqsave(&dev->slock, flags); + viu_start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + mutex_unlock(&dev->lock); + return 0; +} + +static int vidioc_try_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + return 0; +} + +int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + struct v4l2_framebuffer *fb = arg; + + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + return 0; +} + +int vidioc_s_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + struct v4l2_framebuffer *fb = arg; + struct viu_fmt *fmt; + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (fmt == NULL) + return -EINVAL; + + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (dev->ovbuf.fmt.bytesperline == 0) { + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width * fmt->depth / 8; + } + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct viu_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + return videobuf_streamoff(&fh->vb_vidq); +} + +#define decoder_call(viu, o, f, args...) \ + v4l2_subdev_call(viu->decoder, o, f, ##args) + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct viu_fh *fh = priv; + + decoder_call(fh->dev, core, s_std, *id); + return 0; +} + +/* only one input in this driver */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct viu_fh *fh = priv; + + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = fh->dev->vdev->tvnorms; + strcpy(inp->name, "Camera"); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct viu_fh *fh = priv; + + if (i > 1) + return -EINVAL; + + decoder_call(fh->dev, video, s_routing, i, 0, 0); + return 0; +} + +/* Controls */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { + if (qc->id && qc->id == viu_qctrl[i].id) { + memcpy(qc, &(viu_qctrl[i]), sizeof(*qc)); + return 0; + } + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { + if (ctrl->id == viu_qctrl[i].id) { + ctrl->value = qctl_regs[i]; + return 0; + } + } + return -EINVAL; +} +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { + if (ctrl->id == viu_qctrl[i].id) { + if (ctrl->value < viu_qctrl[i].minimum + || ctrl->value > viu_qctrl[i].maximum) + return -ERANGE; + qctl_regs[i] = ctrl->value; + return 0; + } + } + return -EINVAL; +} + +inline void viu_activate_next_buf(struct viu_dev *dev, + struct viu_dmaqueue *viuq) +{ + struct viu_dmaqueue *vidq = viuq; + struct viu_buf *buf; + + /* launch another DMA operation for an active/queued buffer */ + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, + vb.queue); + dprintk(1, "start another queued buffer: 0x%p\n", buf); + buffer_activate(dev, buf); + } else if (!list_empty(&vidq->queued)) { + buf = list_entry(vidq->queued.next, struct viu_buf, + vb.queue); + list_del(&buf->vb.queue); + + dprintk(1, "start another queued buffer: 0x%p\n", buf); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buffer_activate(dev, buf); + } +} + +inline void viu_default_settings(struct viu_reg *viu_reg) +{ + struct viu_reg *vr = viu_reg; + + out_be32(&vr->luminance, 0x9512A254); + out_be32(&vr->chroma_r, 0x03310000); + out_be32(&vr->chroma_g, 0x06600F38); + out_be32(&vr->chroma_b, 0x00000409); + out_be32(&vr->alpha, 0x000000ff); + out_be32(&vr->req_alarm, 0x00000090); + dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", + in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr)); +} + +static void viu_overlay_intr(struct viu_dev *dev, u32 status) +{ + struct viu_reg *vr = dev->vr; + + if (status & INT_DMA_END_STATUS) + dev->dma_done = 1; + + if (status & INT_FIELD_STATUS) { + if (dev->dma_done) { + u32 addr = reg_val.field_base_addr; + + dev->dma_done = 0; + if (status & FIELD_NO) + addr += reg_val.dma_inc; + + out_be32(&vr->field_base_addr, addr); + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg); + } else if (status & INT_VSYNC_STATUS) { + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg); + } + } +} + +static void viu_capture_intr(struct viu_dev *dev, u32 status) +{ + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_reg *vr = dev->vr; + struct viu_buf *buf; + int field_num; + int need_two; + int dma_done = 0; + + field_num = status & FIELD_NO; + need_two = V4L2_FIELD_HAS_BOTH(dev->capfield); + + if (status & INT_DMA_END_STATUS) { + dma_done = 1; + if (((field_num == 0) && (dev->field == 0)) || + (field_num && (dev->field == 1))) + dev->field++; + } + + if (status & INT_FIELD_STATUS) { + dprintk(1, "irq: field %d, done %d\n", + !!field_num, dma_done); + if (unlikely(dev->first)) { + if (field_num == 0) { + dev->first = 0; + dprintk(1, "activate first buf\n"); + viu_activate_next_buf(dev, vidq); + } else + dprintk(1, "wait field 0\n"); + return; + } + + /* setup buffer address for next dma operation */ + if (!list_empty(&vidq->active)) { + u32 addr = reg_val.field_base_addr; + + if (field_num && need_two) { + addr += reg_val.dma_inc; + dprintk(1, "field 1, 0x%lx, dev field %d\n", + (unsigned long)addr, dev->field); + } + out_be32(&vr->field_base_addr, addr); + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg); + return; + } + } + + if (dma_done && field_num && (dev->field == 2)) { + dev->field = 0; + buf = list_entry(vidq->active.next, + struct viu_buf, vb.queue); + dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", + buf, buf->vb.i, + (unsigned long)videobuf_to_dma_contig(&buf->vb), + (unsigned long)in_be32(&vr->field_base_addr)); + + if (waitqueue_active(&buf->vb.done)) { + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + wake_up(&buf->vb.done); + } + /* activate next dma buffer */ + viu_activate_next_buf(dev, vidq); + } +} + +static irqreturn_t viu_intr(int irq, void *dev_id) +{ + struct viu_dev *dev = (struct viu_dev *)dev_id; + struct viu_reg *vr = dev->vr; + u32 status; + u32 error; + + status = in_be32(&vr->status_cfg); + + if (status & INT_ERROR_STATUS) { + dev->irqs.error_irq++; + error = status & ERR_MASK; + if (error) + dprintk(1, "Err: error(%d), times:%d!\n", + error >> 4, dev->irqs.error_irq); + /* Clear interrupt error bit and error flags */ + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | INT_ERROR_STATUS); + } + + if (status & INT_DMA_END_STATUS) { + dev->irqs.dma_end_irq++; + dev->dma_done = 1; + dprintk(2, "VIU DMA end interrupt times: %d\n", + dev->irqs.dma_end_irq); + } + + if (status & INT_HSYNC_STATUS) + dev->irqs.hsync_irq++; + + if (status & INT_FIELD_STATUS) { + dev->irqs.field_irq++; + dprintk(2, "VIU field interrupt times: %d\n", + dev->irqs.field_irq); + } + + if (status & INT_VSTART_STATUS) + dev->irqs.vstart_irq++; + + if (status & INT_VSYNC_STATUS) { + dev->irqs.vsync_irq++; + dprintk(2, "VIU vsync interrupt times: %d\n", + dev->irqs.vsync_irq); + } + + /* clear all pending irqs */ + status = in_be32(&vr->status_cfg); + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | (status & INT_ALL_STATUS)); + + if (dev->ovenable) { + viu_overlay_intr(dev, status); + return IRQ_HANDLED; + } + + /* Capture mode */ + viu_capture_intr(dev, status); + return IRQ_HANDLED; +} + +/* + * File operations for the device + */ +static int viu_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct viu_dev *dev = video_get_drvdata(vdev); + struct viu_fh *fh; + struct viu_reg *vr; + int minor = vdev->minor; + u32 status_cfg; + int i; + + dprintk(1, "viu: open (minor=%d)\n", minor); + + dev->users++; + if (dev->users > 1) { + dev->users--; + return -EBUSY; + } + + vr = dev->vr; + + dprintk(1, "open minor=%d type=%s users=%d\n", minor, + v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); + + /* allocate and initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) { + dev->users--; + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32); + fh->width = norm_maxw(); + fh->height = norm_maxh(); + dev->crop_current.width = fh->width; + dev->crop_current.height = fh->height; + + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) + qctl_regs[i] = viu_qctrl[i].default_value; + + dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&dev->vidq); + dprintk(1, "Open: list_empty queued=%d\n", + list_empty(&dev->vidq.queued)); + dprintk(1, "Open: list_empty active=%d\n", + list_empty(&dev->vidq.active)); + + viu_default_settings(vr); + + status_cfg = in_be32(&vr->status_cfg); + out_be32(&vr->status_cfg, + status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | + INT_FIELD_EN | INT_VSTART_EN | + INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN)); + + status_cfg = in_be32(&vr->status_cfg); + out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS); + + spin_lock_init(&fh->vbq_lock); + videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, + dev->dev, &fh->vbq_lock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct viu_buf), fh); + return 0; +} + +static ssize_t viu_read(struct file *file, char __user *data, size_t count, + loff_t *ppos) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int ret = 0; + + dprintk(2, "%s\n", __func__); + if (dev->ovenable) + dev->ovenable = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + viu_start_dma(dev); + ret = videobuf_read_stream(&fh->vb_vidq, data, count, + ppos, 0, file->f_flags & O_NONBLOCK); + return ret; + } + return 0; +} + +static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait) +{ + struct viu_fh *fh = file->private_data; + struct videobuf_queue *q = &fh->vb_vidq; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + return videobuf_poll_stream(file, q, wait); +} + +static int viu_release(struct file *file) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int minor = video_devdata(file)->minor; + + viu_stop_dma(dev); + videobuf_stop(&fh->vb_vidq); + + kfree(fh); + + dev->users--; + dprintk(1, "close (minor=%d, users=%d)\n", + minor, dev->users); + return 0; +} + +void viu_reset(struct viu_reg *reg) +{ + out_be32(®->status_cfg, 0); + out_be32(®->luminance, 0x9512a254); + out_be32(®->chroma_r, 0x03310000); + out_be32(®->chroma_g, 0x06600f38); + out_be32(®->chroma_b, 0x00000409); + out_be32(®->field_base_addr, 0); + out_be32(®->dma_inc, 0); + out_be32(®->picture_count, 0x01e002d0); + out_be32(®->req_alarm, 0x00000090); + out_be32(®->alpha, 0x000000ff); +} + +static int viu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct viu_fh *fh = file->private_data; + int ret; + + dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + ret); + + return ret; +} + +static struct v4l2_file_operations viu_fops = { + .owner = THIS_MODULE, + .open = viu_open, + .release = viu_release, + .read = viu_read, + .poll = viu_poll, + .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = viu_mmap, +}; + +static const struct v4l2_ioctl_ops viu_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static struct video_device viu_template = { + .name = "FSL viu", + .fops = &viu_fops, + .minor = -1, + .ioctl_ops = &viu_ioctl_ops, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, + .current_norm = V4L2_STD_NTSC_M, +}; + +static int __devinit viu_of_probe(struct platform_device *op, + const struct of_device_id *match) +{ + struct viu_dev *viu_dev; + struct video_device *vdev; + struct resource r; + struct viu_reg __iomem *viu_regs; + struct i2c_adapter *ad; + int ret, viu_irq; + + ret = of_address_to_resource(op->dev.of_node, 0, &r); + if (ret) { + dev_err(&op->dev, "Can't parse device node resource\n"); + return -ENODEV; + } + + viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); + if (viu_irq == NO_IRQ) { + dev_err(&op->dev, "Error while mapping the irq\n"); + return -EINVAL; + } + + /* request mem region */ + if (!devm_request_mem_region(&op->dev, r.start, + sizeof(struct viu_reg), DRV_NAME)) { + dev_err(&op->dev, "Error while requesting mem region\n"); + ret = -EBUSY; + goto err; + } + + /* remap registers */ + viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg)); + if (!viu_regs) { + dev_err(&op->dev, "Can't map register set\n"); + ret = -ENOMEM; + goto err; + } + + /* Prepare our private structure */ + viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC); + if (!viu_dev) { + dev_err(&op->dev, "Can't allocate private structure\n"); + ret = -ENOMEM; + goto err; + } + + viu_dev->vr = viu_regs; + viu_dev->irq = viu_irq; + viu_dev->dev = &op->dev; + + /* init video dma queues */ + INIT_LIST_HEAD(&viu_dev->vidq.active); + INIT_LIST_HEAD(&viu_dev->vidq.queued); + + /* initialize locks */ + mutex_init(&viu_dev->lock); + + snprintf(viu_dev->v4l2_dev.name, + sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); + ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); + if (ret < 0) { + dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); + goto err; + } + + ad = i2c_get_adapter(0); + viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, + "saa7115", "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); + + viu_dev->vidq.timeout.function = viu_vid_timeout; + viu_dev->vidq.timeout.data = (unsigned long)viu_dev; + init_timer(&viu_dev->vidq.timeout); + viu_dev->first = 1; + + /* Allocate memory for video device */ + vdev = video_device_alloc(); + if (vdev == NULL) { + ret = -ENOMEM; + goto err_vdev; + } + + memcpy(vdev, &viu_template, sizeof(viu_template)); + + vdev->v4l2_dev = &viu_dev->v4l2_dev; + + viu_dev->vdev = vdev; + + video_set_drvdata(viu_dev->vdev, viu_dev); + + ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + video_device_release(viu_dev->vdev); + goto err_vdev; + } + + /* enable VIU clock */ + viu_dev->clk = clk_get(&op->dev, "viu_clk"); + if (IS_ERR(viu_dev->clk)) { + dev_err(&op->dev, "failed to find the clock module!\n"); + ret = -ENODEV; + goto err_clk; + } else { + clk_enable(viu_dev->clk); + } + + /* reset VIU module */ + viu_reset(viu_dev->vr); + + /* install interrupt handler */ + if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { + dev_err(&op->dev, "Request VIU IRQ failed.\n"); + ret = -ENODEV; + goto err_irq; + } + + dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); + return ret; + +err_irq: + clk_disable(viu_dev->clk); + clk_put(viu_dev->clk); +err_clk: + video_unregister_device(viu_dev->vdev); +err_vdev: + i2c_put_adapter(ad); + v4l2_device_unregister(&viu_dev->v4l2_dev); +err: + irq_dispose_mapping(viu_irq); + return ret; +} + +static int __devexit viu_of_remove(struct platform_device *op) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sdev); + + free_irq(dev->irq, (void *)dev); + irq_dispose_mapping(dev->irq); + + clk_disable(dev->clk); + clk_put(dev->clk); + + video_unregister_device(dev->vdev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&dev->v4l2_dev); + return 0; +} + +#ifdef CONFIG_PM +static int viu_suspend(struct platform_device *op, pm_message_t state) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_disable(dev->clk); + return 0; +} + +static int viu_resume(struct platform_device *op) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_enable(dev->clk); + return 0; +} +#endif + +/* + * Initialization and module stuff + */ +static struct of_device_id mpc512x_viu_of_match[] = { + { + .compatible = "fsl,mpc5121-viu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match); + +static struct of_platform_driver viu_of_platform_driver = { + .probe = viu_of_probe, + .remove = __devexit_p(viu_of_remove), +#ifdef CONFIG_PM + .suspend = viu_suspend, + .resume = viu_resume, +#endif + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = mpc512x_viu_of_match, + }, +}; + +static int __init viu_init(void) +{ + return of_register_platform_driver(&viu_of_platform_driver); +} + +static void __exit viu_exit(void) +{ + of_unregister_platform_driver(&viu_of_platform_driver); +} + +module_init(viu_init); +module_exit(viu_exit); + +MODULE_DESCRIPTION("Freescale Video-In(VIU)"); +MODULE_AUTHOR("Hongjun Chen"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 5d920e584de..23db0c29f68 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -246,6 +246,15 @@ config USB_GSPCA_SPCA561 To compile this driver as a module, choose M here: the module will be called gspca_spca561. +config USB_GSPCA_SPCA1528 + tristate "SPCA1528 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA1528 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca1528. + config USB_GSPCA_SQ905 tristate "SQ Technologies SQ905 based USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -264,6 +273,15 @@ config USB_GSPCA_SQ905C To compile this driver as a module, choose M here: the module will be called gspca_sq905c. +config USB_GSPCA_SQ930X + tristate "SQ Technologies SQ930X based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ930X chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq930x. + config USB_GSPCA_STK014 tristate "Syntek DV4000 (STK014) USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 6e4cf1ce01c..f6616db0b7f 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -23,8 +23,10 @@ obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o +obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o @@ -58,8 +60,10 @@ gspca_spca505-objs := spca505.o gspca_spca506-objs := spca506.o gspca_spca508-objs := spca508.o gspca_spca561-objs := spca561.o +gspca_spca1528-objs := spca1528.o gspca_sq905-objs := sq905.o gspca_sq905c-objs := sq905c.o +gspca_sq930x-objs := sq930x.o gspca_stk014-objs := stk014.o gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 19fe6b24c9a..d6a75772f3f 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -41,7 +41,7 @@ struct sd { #define QUALITY_MAX 60 #define QUALITY_DEF 40 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -845,9 +845,6 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -862,11 +859,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; int retry = 50; - kfree(sd->jpeg_hdr); - if (!gspca_dev->present) return; reg_w_val(gspca_dev, 0x0000, 0x00); diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 58b696f455b..3747a1dcff5 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -1760,22 +1760,19 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data[25] == sd->params.roi.colEnd && data[26] == sd->params.roi.rowStart && data[27] == sd->params.roi.rowEnd) { - struct gspca_frame *frame = gspca_get_i_frame(gspca_dev); + u8 *image; atomic_set(&sd->cam_exposure, data[39] * 2); atomic_set(&sd->fps, data[41]); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* Check for proper EOF for last frame */ - if ((frame->data_end - frame->data) > 4 && - frame->data_end[-4] == 0xff && - frame->data_end[-3] == 0xff && - frame->data_end[-2] == 0xff && - frame->data_end[-1] == 0xff) + image = gspca_dev->image; + if (image != NULL && + gspca_dev->image_len > 4 && + image[gspca_dev->image_len - 4] == 0xff && + image[gspca_dev->image_len - 3] == 0xff && + image[gspca_dev->image_len - 2] == 0xff && + image[gspca_dev->image_len - 1] == 0xff) gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c index 7c31b4f2abe..57782e011c9 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi2020.c +++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -1,6 +1,7 @@ /* Subdriver for the GL860 chip with the MI2020 sensor - * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's - * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. + * Author Olivier LORIN, from logs by Iceman/Soro2005 + Fret_saw/Hulkie/Tricid + * with the help of Kytrix/BUGabundo/Blazercist. + * Driver achieved thanks to a webcam gift by Kytrix. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,47 +21,70 @@ #include "gl860.h" +static u8 dat_wbal1[] = {0x8c, 0xa2, 0x0c}; + static u8 dat_bright1[] = {0x8c, 0xa2, 0x06}; static u8 dat_bright3[] = {0x8c, 0xa1, 0x02}; static u8 dat_bright4[] = {0x90, 0x00, 0x0f}; static u8 dat_bright5[] = {0x8c, 0xa1, 0x03}; static u8 dat_bright6[] = {0x90, 0x00, 0x05}; -static u8 dat_dummy1[] = {0x90, 0x00, 0x06}; -/*static u8 dummy2[] = {0x8c, 0xa1, 0x02};*/ -/*static u8 dummy3[] = {0x90, 0x00, 0x1f};*/ - static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19}; static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b}; static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; +static struct idxdata tbl_middle_hvflip_low[] = { + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_middle_hvflip_big[] = { + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {102, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, +}; + +static struct idxdata tbl_end_hvflip[] = { + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, +}; + static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; -static struct validx tbl_common1[] = { - {0x0000, 0x0000}, - {1, 0xffff}, /* msleep(35); */ - {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, - {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0004, 0x00d8}, - {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001,0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + {53, 0xffff}, + {0x0040, 0x0000}, {0x0063, 0x0006}, }; -static struct validx tbl_common2[] = { - {0x006a, 0x0007}, - {35, 0xffff}, - {0x00ef, 0x0006}, - {35, 0xffff}, - {0x006a, 0x000d}, - {35, 0xffff}, - {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, +static struct validx tbl_common_0B[] = { + {0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a,0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042,0x00c2}, {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, }; -static struct idxdata tbl_common3[] = { - {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, +static struct idxdata tbl_common_3B[] = { + {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"}, + {2, "\xff\xff\xff"}, + {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, {6, "\xff\xff\xff"}, /* 12 */ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, {2, "\xff\xff\xff"}, /* - */ @@ -98,85 +122,58 @@ static struct idxdata tbl_common3[] = { {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, - {1, "\xff\xff\xff"}, {0x33, "\x78\x00\x00"}, - {1, "\xff\xff\xff"}, + {2, "\xff\xff\xff"}, {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, - {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, -}; - -static struct idxdata tbl_common4[] = { - {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, + {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"}, + {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, + {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"}, + {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"}, + {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"}, + {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"}, + {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"}, + {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"}, + {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"}, + {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"}, + {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"}, + {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"}, + {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"}, + {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"}, + {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"}, + {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"}, + {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"}, + {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, + {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"}, + {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"}, + {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"}, + {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"}, + {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"}, + {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, - {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa0"}, - {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc0"}, {0x33, "\x8c\x24\x15"}, - {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, -}; - -static struct idxdata tbl_common5[] = { - {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, - {0x33, "\x90\x00\x04"}, {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, - /* msleep(53); */ - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, - {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, - {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, - {0x33, "\x90\x02\x84"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, - {0x33, "\x8c\x27\x07"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, - {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x0f"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, - {0x33, "\x90\x04\xbd"}, {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, - {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, - {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, - {0x33, "\x90\x01\x02"}, {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, - {0x33, "\x8c\x27\x21"}, {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, - {0x33, "\x90\x02\x85"}, {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, - {0x33, "\x8c\x27\x27"}, {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, - {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, - {0x33, "\x8c\x27\x2d"}, {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, - {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, - {0x33, "\x8c\x27\x33"}, {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, - {0x33, "\x90\x06\x4b"}, {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, - {0x33, "\x90\x00\x24"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, - {0x33, "\x8c\x27\x41"}, {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, - {0x33, "\x90\x04\xed"}, {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, - {0x33, "\x8c\x27\x51"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, - {0x33, "\x90\x03\x20"}, {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x57"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, - {0x33, "\x8c\x27\x63"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, - {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, - {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, - {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, - {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, - {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, - {0x33, "\x8c\x24\x15"}, -}; - -static struct validx tbl_init_at_startup[] = { - {0x0000, 0x0000}, - {53, 0xffff}, - {0x0010, 0x0010}, - {53, 0xffff}, - {0x0008, 0x00c0}, - {53, 0xffff}, - {0x0001, 0x00c1}, - {53, 0xffff}, - {0x0001, 0x00c2}, - {53, 0xffff}, - {0x0020, 0x0006}, - {53, 0xffff}, - {0x006a, 0x000d}, - {53, 0xffff}, + {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"}, + {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"}, + {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {3, "\xff\xff\xff"}, }; static struct idxdata tbl_init_post_alt_low1[] = { @@ -209,7 +206,7 @@ static struct idxdata tbl_init_post_alt_low3[] = { {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {2, "\xff\xff\xff"}, /* - * */ + {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, @@ -217,61 +214,15 @@ static struct idxdata tbl_init_post_alt_low3[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {1, "\xff\xff\xff"}, -}; - -static struct idxdata tbl_init_post_alt_low4[] = { - {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, - {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, - {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, - {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, - {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, - {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, - {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, - {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, - {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, - {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, - {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, - {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, - {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, - {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, - {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, - {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, - {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, - {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, - {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, - {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, - {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, - {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, - {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, - {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, - {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, - {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, - /* Flip/Mirror h/v=1 */ - {0x33, "\x90\x00\x3c"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, - {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x06"}, - {130, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, - {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, - {100, "\xff\xff\xff"}, - /* ?? */ - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - /* Brigthness=70 */ - {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x46"}, {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x0f"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - /* Sharpness=20 */ - {0x32, "\x6c\x14\x08"}, }; -static struct idxdata tbl_init_post_alt_big1[] = { +static struct idxdata tbl_init_post_alt_big[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, @@ -285,9 +236,17 @@ static struct idxdata tbl_init_post_alt_big1[] = { {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"}, + {51, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {51, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {51, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_big2[] = { +static struct idxdata tbl_init_post_alt_3B[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -316,17 +275,6 @@ static struct idxdata tbl_init_post_alt_big2[] = { {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, }; -static struct idxdata tbl_init_post_alt_big3[] = { - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, -}; - static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21"; static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01"; @@ -351,7 +299,7 @@ void mi2020_init_settings(struct gspca_dev *gspca_dev) sd->vcur.gamma = 0; sd->vcur.hue = 0; sd->vcur.saturation = 60; - sd->vcur.whitebal = 50; + sd->vcur.whitebal = 0; /* 50, not done by hardware */ sd->vcur.mirror = 0; sd->vcur.flip = 0; sd->vcur.AC50Hz = 1; @@ -361,17 +309,12 @@ void mi2020_init_settings(struct gspca_dev *gspca_dev) sd->vmax.sharpness = 40; sd->vmax.contrast = 3; sd->vmax.gamma = 2; - sd->vmax.hue = 0 + 1; /* 200 */ - sd->vmax.saturation = 0; /* 100 */ - sd->vmax.whitebal = 0; /* 100 */ + sd->vmax.hue = 0 + 1; /* 200, not done by hardware */ + sd->vmax.saturation = 0; /* 100, not done by hardware */ + sd->vmax.whitebal = 2; /* 100, not done by hardware */ sd->vmax.mirror = 1; sd->vmax.flip = 1; sd->vmax.AC50Hz = 1; - if (_MI2020b_) { - sd->vmax.contrast = 0; - sd->vmax.gamma = 0; - sd->vmax.backlight = 0; - } sd->dev_camera_settings = mi2020_camera_settings; sd->dev_init_at_startup = mi2020_init_at_startup; @@ -384,51 +327,9 @@ void mi2020_init_settings(struct gspca_dev *gspca_dev) static void common(struct gspca_dev *gspca_dev) { - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - if (_MI2020b_) { - fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1)); - } else { - if (_MI2020_) - ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); - else - ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); - msleep(35); - fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2)); - } - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); - msleep(2); /* - * */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); - if (reso == IMAGE_1600) - msleep(2); /* 1600 */ - fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3)); - - if (_MI2020b_ || _MI2020_) - fetch_idxdata(gspca_dev, tbl_common4, - ARRAY_SIZE(tbl_common4)); - - fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5)); - if (_MI2020b_ || _MI2020_) { - /* Different from fret */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); - /* Same as fret */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); - /* Different from fret */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x90"); - } else { - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x6a"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x80"); - } - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x05"); - msleep(2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - if (reso == IMAGE_1600) - msleep(14); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x06"); - msleep(2); + fetch_validx(gspca_dev, tbl_common_0B, ARRAY_SIZE(tbl_common_0B)); + fetch_idxdata(gspca_dev, tbl_common_3B, ARRAY_SIZE(tbl_common_3B)); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); } static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) @@ -441,8 +342,16 @@ static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) fetch_validx(gspca_dev, tbl_init_at_startup, ARRAY_SIZE(tbl_init_at_startup)); + ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &c); + common(gspca_dev); + msleep(61); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ +/* msleep(36); */ + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); + return 0; } @@ -450,17 +359,17 @@ static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sd->mirrorMask = 0; + sd->mirrorMask = 0; + sd->vold.hue = -1; - sd->vold.backlight = -1; + /* These controls need to be reset */ sd->vold.brightness = -1; sd->vold.sharpness = -1; - sd->vold.contrast = -1; - sd->vold.gamma = -1; - sd->vold.hue = -1; - sd->vold.mirror = -1; - sd->vold.flip = -1; - sd->vold.AC50Hz = -1; + + /* If not different from default, they do not need to be set */ + sd->vold.contrast = 0; + sd->vold.gamma = 0; + sd->vold.backlight = 0; mi2020_init_post_alt(gspca_dev); @@ -472,10 +381,10 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 backlight = sd->vcur.backlight; s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); s32 freq = (sd->vcur.AC50Hz > 0); + s32 wbal = sd->vcur.whitebal; u8 dat_freq2[] = {0x90, 0x00, 0x80}; u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; @@ -484,6 +393,7 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) u8 dat_multi4[] = {0x90, 0x00, 0x00}; u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 dat_wbal2[] = {0x90, 0x00, 0x00}; u8 c; sd->nbIm = -1; @@ -491,23 +401,26 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) dat_freq2[2] = freq ? 0xc0 : 0x80; dat_multi1[2] = 0x9d; dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = backlight; + if (wbal == 0) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x17; + } else if (wbal == 1) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x35; + } else if (wbal == 2) { + dat_multi4[2] = dat_multi2[2] = 0x20; + dat_wbal2[2] = 0x17; + } dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); msleep(200); - ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - msleep(3); /* 35 * */ + msleep(2); common(gspca_dev); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); - msleep(70); - - if (_MI2020b_) - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - + msleep(142); ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL); @@ -523,8 +436,7 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800); - if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, + fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, ARRAY_SIZE(tbl_init_post_alt_low1)); if (reso == IMAGE_800) @@ -534,87 +446,10 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, ARRAY_SIZE(tbl_init_post_alt_low3)); - if (_MI2020b_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(150); - } else if (_MI2020c_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(120); - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(30); - } else if (_MI2020_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(120); - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(30); - } - - /* AC power frequency */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); - msleep(20); - /* backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - /* at init time but not after */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); - /* finish the backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - msleep(5);/* " */ - - if (_MI2020c_) { - fetch_idxdata(gspca_dev, tbl_init_post_alt_low4, - ARRAY_SIZE(tbl_init_post_alt_low4)); - } else { - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); - msleep(14); /* 0xd8 */ - - /* flip/mirror */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip6); - msleep(21); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - /* end of flip/mirror main part */ - msleep(246); /* 146 */ - - sd->nbIm = 0; - } + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); break; case IMAGE_1280: @@ -643,108 +478,62 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 3, "\x90\x04\xb0"); } - fetch_idxdata(gspca_dev, tbl_init_post_alt_big1, - ARRAY_SIZE(tbl_init_post_alt_big1)); - - if (reso == IMAGE_1600) - msleep(13); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x27\x97"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x01\x00"); - msleep(53); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - if (reso == IMAGE_1600) - msleep(13); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - msleep(53); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); - if (reso == IMAGE_1600) - msleep(13); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - msleep(53); - - if (_MI2020b_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - if (reso == IMAGE_1600) - msleep(500); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(1850); - } else if (_MI2020c_ || _MI2020_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(1850); - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(30); - } + fetch_idxdata(gspca_dev, tbl_init_post_alt_big, + ARRAY_SIZE(tbl_init_post_alt_big)); - /* AC power frequency */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); - msleep(20); - /* backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - /* at init time but not after */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); - /* finish the backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - msleep(6); /* " */ + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + } - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); - msleep(14); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(40); + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(33); + /* light source */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(7); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, + ARRAY_SIZE(tbl_init_post_alt_3B)); + + /* hvflip */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + msleep(250); + + if (reso == IMAGE_640 || reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, + ARRAY_SIZE(tbl_middle_hvflip_low)); + else + fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, + ARRAY_SIZE(tbl_middle_hvflip_big)); - if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big2, - ARRAY_SIZE(tbl_init_post_alt_big2)); + fetch_idxdata(gspca_dev, tbl_end_hvflip, + ARRAY_SIZE(tbl_end_hvflip)); - /* flip/mirror */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); - /* end of flip/mirror main part */ - msleep(16); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - if (reso == IMAGE_1600) - msleep(25); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - msleep(103); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - sd->nbIm = 0; - - if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big3, - ARRAY_SIZE(tbl_init_post_alt_big3)); - } + sd->nbIm = 0; sd->vold.mirror = mirror; sd->vold.flip = flip; sd->vold.AC50Hz = freq; - sd->vold.backlight = backlight; + sd->vold.whitebal = wbal; mi2020_camera_settings(gspca_dev); @@ -772,6 +561,7 @@ static int mi2020_configure_alt(struct gspca_dev *gspca_dev) static int mi2020_camera_settings(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; s32 backlight = sd->vcur.backlight; s32 bright = sd->vcur.brightness; @@ -782,6 +572,7 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); s32 freq = (sd->vcur.AC50Hz > 0); + s32 wbal = sd->vcur.whitebal; u8 dat_sharp[] = {0x6c, 0x00, 0x08}; u8 dat_bright2[] = {0x90, 0x00, 0x00}; @@ -792,6 +583,7 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) u8 dat_multi4[] = {0x90, 0x00, 0x00}; u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 dat_wbal2[] = {0x90, 0x00, 0x00}; /* Less than 4 images received -> too early to set the settings */ if (sd->nbIm < 4) { @@ -809,67 +601,89 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) msleep(20); } + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + if (wbal == 0) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x17; + } else if (wbal == 1) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x35; + } else if (wbal == 2) { + dat_multi4[2] = dat_multi2[2] = 0x20; + dat_wbal2[2] = 0x17; + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { sd->vold.mirror = mirror; sd->vold.flip = flip; dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, + ARRAY_SIZE(tbl_init_post_alt_3B)); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); - msleep(130); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - - /* Sometimes present, sometimes not, useful? */ - /* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);*/ + msleep(40); + + if (reso == IMAGE_640 || reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, + ARRAY_SIZE(tbl_middle_hvflip_low)); + else + fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, + ARRAY_SIZE(tbl_middle_hvflip_big)); + + fetch_idxdata(gspca_dev, tbl_end_hvflip, + ARRAY_SIZE(tbl_end_hvflip)); } - if (backlight != sd->vold.backlight) { - sd->vold.backlight = backlight; - if (backlight < 0 || backlight > sd->vmax.backlight) - backlight = 0; + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; - dat_multi1[2] = 0x9d; - dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = backlight; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + dat_bright2[2] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); } - if (gam != sd->vold.gamma) { + if (cntr != sd->vold.contrast || gam != sd->vold.gamma) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; sd->vold.gamma = gam; if (gam < 0 || gam > sd->vmax.gamma) gam = 0; dat_multi1[2] = 0x6d; dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = 0x40 + gam; + if (cntr == 0) + cntr = 4; + dat_multi4[2] = dat_multi2[2] = cntr * 0x10 + 2 - gam; ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); @@ -878,14 +692,14 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); } - if (cntr != sd->vold.contrast) { - sd->vold.contrast = cntr; - if (cntr < 0 || cntr > sd->vmax.contrast) - cntr = 0; + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; - dat_multi1[2] = 0x6d; + dat_multi1[2] = 0x9d; dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = 0x12 + 16 * cntr; + dat_multi4[2] = dat_multi2[2] = backlight; ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); @@ -894,20 +708,6 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); } - if (bright != sd->vold.brightness) { - sd->vold.brightness = bright; - if (bright < 0 || bright > sd->vmax.brightness) - bright = 0; - - dat_bright2[2] = bright; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); - } - if (sharp != sd->vold.sharpness) { sd->vold.sharpness = sharp; if (sharp < 0 || sharp > sd->vmax.sharpness) @@ -928,9 +728,6 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev) { ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); - msleep(20); - if (_MI2020c_ || _MI2020_) - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); - else - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + msleep(40); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); } diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c index d412694c50a..5ae9619d72a 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov9655.c +++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -69,7 +69,7 @@ static u8 *tbl_640[] = { "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80" }; -static u8 *tbl_800[] = { +static u8 *tbl_1280[] = { "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" , "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61" @@ -217,7 +217,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - tbl = (reso == IMAGE_640) ? tbl_640 : tbl_800; + tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280; ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, tbl_length[0], tbl[0]); diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 9e42476c0ea..e86eb8b4aed 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -63,7 +63,7 @@ static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ \ sd->vcur.thename = val;\ if (gspca_dev->streaming)\ - sd->dev_camera_settings(gspca_dev);\ + sd->waitSet = 1;\ return 0;\ } \ static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ @@ -91,7 +91,6 @@ SD_SETGET(contrast) /* control table */ static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; -static struct ctrl sd_ctrls_mi2020b[GL860_NCTRLS]; static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; @@ -121,8 +120,6 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev) sd_ctrls = sd_ctrls_mi1320; else if (_MI2020_) sd_ctrls = sd_ctrls_mi2020; - else if (_MI2020b_) - sd_ctrls = sd_ctrls_mi2020b; else if (_OV2640_) sd_ctrls = sd_ctrls_ov2640; else if (_OV9655_) @@ -187,19 +184,6 @@ static const struct sd_desc sd_desc_mi2020 = { .dq_callback = sd_callback, }; -static const struct sd_desc sd_desc_mi2020b = { - .name = MODULE_NAME, - .ctrls = sd_ctrls_mi2020b, - .nctrls = GL860_NCTRLS, - .config = sd_config, - .init = sd_init, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_callback, -}; - static const struct sd_desc sd_desc_ov2640 = { .name = MODULE_NAME, .ctrls = sd_ctrls_ov2640, @@ -235,9 +219,9 @@ static struct v4l2_pix_format mi2020_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0 }, - { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 800, - .sizeimage = 800 * 600, + .sizeimage = 800 * 598, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1 }, @@ -247,9 +231,9 @@ static struct v4l2_pix_format mi2020_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2 }, - {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 1600, - .sizeimage = 1600 * 1200, + .sizeimage = 1600 * 1198, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 3 }, @@ -344,8 +328,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = ID_OV9655; else if (strcmp(sensor, "MI2020") == 0) sd->sensor = ID_MI2020; - else if (strcmp(sensor, "MI2020b") == 0) - sd->sensor = ID_MI2020b; /* Get sensor and set the suitable init/start/../stop functions */ if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) @@ -369,13 +351,6 @@ static int sd_config(struct gspca_dev *gspca_dev, dev_init_settings = mi2020_init_settings; break; - case ID_MI2020b: - gspca_dev->sd_desc = &sd_desc_mi2020b; - cam->cam_mode = mi2020_mode; - cam->nmodes = ARRAY_SIZE(mi2020_mode); - dev_init_settings = mi2020_init_settings; - break; - case ID_OV2640: gspca_dev->sd_desc = &sd_desc_ov2640; cam->cam_mode = ov2640_mode; @@ -620,10 +595,7 @@ int gl860_RTx(struct gspca_dev *gspca_dev, else if (len > 1 && r < len) PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); - if ((_MI2020_ || _MI2020b_ || _MI2020c_) && (val || index)) - msleep(1); - if (_OV2640_) - msleep(1); + msleep(1); return r; } @@ -767,8 +739,6 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); } else if (_MI2020_) { PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); - } else if (_MI2020b_) { - PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 alt. driver (2.0M)"); } else if (_OV9655_) { PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); } else if (_OV2640_) { diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h index 305061ff838..49ad4acbf60 100644 --- a/drivers/media/video/gspca/gl860/gl860.h +++ b/drivers/media/video/gspca/gl860/gl860.h @@ -32,19 +32,16 @@ #define ID_OV2640 2 #define ID_OV9655 4 #define ID_MI2020 8 -#define ID_MI2020b 16 #define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320) #define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020) -#define _MI2020b_ (((struct sd *) gspca_dev)->sensor == ID_MI2020b) -#define _MI2020c_ 0 #define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640) #define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655) #define IMAGE_640 0 #define IMAGE_800 1 #define IMAGE_1280 2 -#define IMAGE_1600 3 +#define IMAGE_1600 3 struct sd_gl860 { u16 backlight; @@ -75,10 +72,10 @@ struct sd { int (*dev_camera_settings)(struct gspca_dev *); u8 swapRB; - u8 mirrorMask; - u8 sensor; - s32 nbIm; - s32 nbRightUp; + u8 mirrorMask; + u8 sensor; + s32 nbIm; + s32 nbRightUp; u8 waitSet; }; diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 678675bb365..b9846106913 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 9, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 10, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -201,7 +201,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, buffer_len = le16_to_cpu(ep->wMaxPacketSize); interval = ep->bInterval; - PDEBUG(D_PROBE, "found int in endpoint: 0x%x, " + PDEBUG(D_CONF, "found int in endpoint: 0x%x, " "buffer_len=%u, interval=%u", ep->bEndpointAddress, buffer_len, interval); @@ -226,7 +226,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, gspca_dev->int_urb = urb; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) { - PDEBUG(D_ERR, "submit URB failed with error %i", ret); + PDEBUG(D_ERR, "submit int URB failed with error %i", ret); goto error_submit; } return ret; @@ -294,19 +294,6 @@ static inline int gspca_input_connect(struct gspca_dev *dev) } #endif -/* get the current input frame buffer */ -struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev) -{ - struct gspca_frame *frame; - - frame = gspca_dev->cur_frame; - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) - return NULL; - return frame; -} -EXPORT_SYMBOL(gspca_get_i_frame); - /* * fill a video frame from an URB and resubmit */ @@ -439,56 +426,70 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); - /* check the availability of the frame buffer */ - frame = gspca_dev->cur_frame; - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - /* when start of a new frame, if the current frame buffer - * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { - frame->data_end = frame->data; + i = atomic_read(&gspca_dev->fr_i); + + /* if there are no queued buffer, discard the whole frame */ + if (i == atomic_read(&gspca_dev->fr_q)) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); frame->v4l2_buf.sequence = ++gspca_dev->sequence; - } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { - if (packet_type == LAST_PACKET) - gspca_dev->last_packet_type = packet_type; - return; + gspca_dev->image = frame->data; + gspca_dev->image_len = 0; + } else { + switch (gspca_dev->last_packet_type) { + case DISCARD_PACKET: + if (packet_type == LAST_PACKET) + gspca_dev->last_packet_type = packet_type; + return; + case LAST_PACKET: + return; + } } /* append the packet to the frame buffer */ if (len > 0) { - if (frame->data_end - frame->data + len - > frame->v4l2_buf.length) { - PDEBUG(D_ERR|D_PACK, "frame overflow %zd > %d", - frame->data_end - frame->data + len, - frame->v4l2_buf.length); + if (gspca_dev->image_len + len > gspca_dev->frsz) { + PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d", + gspca_dev->image_len + len, + gspca_dev->frsz); packet_type = DISCARD_PACKET; } else { - memcpy(frame->data_end, data, len); - frame->data_end += len; +/* !! image is NULL only when last pkt is LAST or DISCARD + if (gspca_dev->image == NULL) { + err("gspca_frame_add() image == NULL"); + return; + } + */ + memcpy(gspca_dev->image + gspca_dev->image_len, + data, len); + gspca_dev->image_len += len; } } gspca_dev->last_packet_type = packet_type; - /* if last packet, wake up the application and advance in the queue */ + /* if last packet, invalidate packet concatenation until + * next first packet, wake up the application and advance + * in the queue */ if (packet_type == LAST_PACKET) { - frame->v4l2_buf.bytesused = frame->data_end - frame->data; - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ - i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; - gspca_dev->fr_i = i; - PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d", - frame->v4l2_buf.bytesused, - gspca_dev->fr_q, - i, - gspca_dev->fr_o); + i = atomic_read(&gspca_dev->fr_i); j = gspca_dev->fr_queue[i]; - gspca_dev->cur_frame = &gspca_dev->frame[j]; + frame = &gspca_dev->frame[j]; + frame->v4l2_buf.bytesused = gspca_dev->image_len; + frame->v4l2_buf.flags = (frame->v4l2_buf.flags + | V4L2_BUF_FLAG_DONE) + & ~V4L2_BUF_FLAG_QUEUED; + i = (i + 1) % GSPCA_MAX_FRAMES; + atomic_set(&gspca_dev->fr_i, i); + wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ + PDEBUG(D_FRAM, "frame complete len:%d", + frame->v4l2_buf.bytesused); + gspca_dev->image = NULL; + gspca_dev->image_len = 0; } } EXPORT_SYMBOL(gspca_frame_add); @@ -506,36 +507,6 @@ static int gspca_is_compressed(__u32 format) return 0; } -static void *rvmalloc(long size) -{ - void *mem; - unsigned long adr; - - mem = vmalloc_32(size); - if (mem != NULL) { - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *) adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void *mem, long size) -{ - unsigned long adr; - - adr = (unsigned long) mem; - while (size > 0) { - ClearPageReserved(vmalloc_to_page((void *) adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - static int frame_alloc(struct gspca_dev *gspca_dev, unsigned int count) { @@ -548,9 +519,9 @@ static int frame_alloc(struct gspca_dev *gspca_dev, PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); frsz = PAGE_ALIGN(frsz); gspca_dev->frsz = frsz; - if (count > GSPCA_MAX_FRAMES) - count = GSPCA_MAX_FRAMES; - gspca_dev->frbuf = rvmalloc(frsz * count); + if (count >= GSPCA_MAX_FRAMES) + count = GSPCA_MAX_FRAMES - 1; + gspca_dev->frbuf = vmalloc_32(frsz * count); if (!gspca_dev->frbuf) { err("frame alloc failed"); return -ENOMEM; @@ -565,14 +536,12 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frame->v4l2_buf.length = frsz; frame->v4l2_buf.memory = gspca_dev->memory; frame->v4l2_buf.sequence = 0; - frame->data = frame->data_end = - gspca_dev->frbuf + i * frsz; + frame->data = gspca_dev->frbuf + i * frsz; frame->v4l2_buf.m.offset = i * frsz; } - gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; - gspca_dev->cur_frame = &gspca_dev->frame[0]; - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; + atomic_set(&gspca_dev->fr_q, 0); + atomic_set(&gspca_dev->fr_i, 0); + gspca_dev->fr_o = 0; return 0; } @@ -582,8 +551,7 @@ static void frame_free(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "frame free"); if (gspca_dev->frbuf != NULL) { - rvfree(gspca_dev->frbuf, - gspca_dev->nframes * gspca_dev->frsz); + vfree(gspca_dev->frbuf); gspca_dev->frbuf = NULL; for (i = 0; i < gspca_dev->nframes; i++) gspca_dev->frame[i].data = NULL; @@ -683,12 +651,16 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) : USB_ENDPOINT_XFER_ISOC; i = gspca_dev->alt; /* previous alt setting */ if (gspca_dev->cam.reverse_alts) { + if (gspca_dev->audio) + i++; while (++i < gspca_dev->nbalt) { ep = alt_xfer(&intf->altsetting[i], xfer); if (ep) break; } } else { + if (gspca_dev->audio) + i--; while (--i >= 0) { ep = alt_xfer(&intf->altsetting[i], xfer); if (ep) @@ -811,6 +783,12 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; } + /* reset the streaming variables */ + gspca_dev->image = NULL; + gspca_dev->image_len = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence = 0; + gspca_dev->usb_err = 0; /* set the higher alternate setting and @@ -1433,34 +1411,6 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return ret; } -/*fixme: have an audio flag in gspca_dev?*/ -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - if (audio->index != 0) - return -EINVAL; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - strcpy(audio->name, "Microphone"); - return 0; -} - -static int vidioc_enumaudio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - if (audio->index != 0) - return -EINVAL; - - strcpy(audio->name, "Microphone"); - audio->capability = 0; - audio->mode = 0; - return 0; -} - static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { @@ -1504,7 +1454,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int i, ret = 0, streaming; - switch (rb->memory) { + i = rb->memory; /* (avoid compilation warning) */ + switch (i) { case GSPCA_MEMORY_READ: /* (internal call) */ case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: @@ -1626,7 +1577,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type) { struct gspca_dev *gspca_dev = priv; - int i, ret; + int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1650,12 +1601,10 @@ static int vidioc_streamoff(struct file *file, void *priv, gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); - /* empty the application queues */ - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; - gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; + /* empty the transfer queues */ + atomic_set(&gspca_dev->fr_q, 0); + atomic_set(&gspca_dev->fr_i, 0); + gspca_dev->fr_o = 0; ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1732,7 +1681,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, int n; n = parm->parm.capture.readbuffers; - if (n == 0 || n > GSPCA_MAX_FRAMES) + if (n == 0 || n >= GSPCA_MAX_FRAMES) parm->parm.capture.readbuffers = gspca_dev->nbufread; else gspca_dev->nbufread = n; @@ -1755,49 +1704,6 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -#ifdef CONFIG_VIDEO_V4L1_COMPAT -static int vidiocgmbuf(struct file *file, void *priv, - struct video_mbuf *mbuf) -{ - struct gspca_dev *gspca_dev = file->private_data; - int i; - - PDEBUG(D_STREAM, "cgmbuf"); - if (gspca_dev->nframes == 0) { - int ret; - - { - struct v4l2_format fmt; - - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - i = gspca_dev->cam.nmodes - 1; /* highest mode */ - fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; - fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; - ret = vidioc_s_fmt_vid_cap(file, priv, &fmt); - if (ret != 0) - return ret; - } - { - struct v4l2_requestbuffers rb; - - memset(&rb, 0, sizeof rb); - rb.count = 4; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = V4L2_MEMORY_MMAP; - ret = vidioc_reqbufs(file, priv, &rb); - if (ret != 0) - return ret; - } - } - mbuf->frames = gspca_dev->nframes; - mbuf->size = gspca_dev->frsz * gspca_dev->nframes; - for (i = 0; i < mbuf->frames; i++) - mbuf->offsets[i] = gspca_dev->frame[i].v4l2_buf.m.offset; - return 0; -} -#endif - static int dev_mmap(struct file *file, struct vm_area_struct *vma) { struct gspca_dev *gspca_dev = file->private_data; @@ -1838,12 +1744,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) ret = -EINVAL; goto out; } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - /* v4l1 maps all the buffers */ - if (i != 0 - || size != frame->v4l2_buf.length * gspca_dev->nframes) -#endif - if (size != frame->v4l2_buf.length) { + if (size != frame->v4l2_buf.length) { PDEBUG(D_STREAM, "mmap bad size"); ret = -EINVAL; goto out; @@ -1883,21 +1784,17 @@ out: static int frame_wait(struct gspca_dev *gspca_dev, int nonblock_ing) { - struct gspca_frame *frame; - int i, j, ret; + int i, ret; /* check if a frame is ready */ i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - - if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) { + if (i == atomic_read(&gspca_dev->fr_i)) { if (nonblock_ing) return -EAGAIN; /* wait till a frame is ready */ ret = wait_event_interruptible_timeout(gspca_dev->wq, - (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) || + i != atomic_read(&gspca_dev->fr_i) || !gspca_dev->streaming || !gspca_dev->present, msecs_to_jiffies(3000)); if (ret < 0) @@ -1906,11 +1803,7 @@ static int frame_wait(struct gspca_dev *gspca_dev, return -EIO; } - gspca_dev->fr_o = (i + 1) % gspca_dev->nframes; - PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - gspca_dev->fr_o); + gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; if (gspca_dev->sd_desc->dq_callback) { mutex_lock(&gspca_dev->usb_lock); @@ -1919,7 +1812,7 @@ static int frame_wait(struct gspca_dev *gspca_dev, gspca_dev->sd_desc->dq_callback(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } - return j; + return gspca_dev->fr_queue[i]; } /* @@ -2024,15 +1917,9 @@ static int vidioc_qbuf(struct file *file, void *priv, } /* put the buffer in the 'queued' queue */ - i = gspca_dev->fr_q; + i = atomic_read(&gspca_dev->fr_q); gspca_dev->fr_queue[i] = index; - if (gspca_dev->fr_i == i) - gspca_dev->cur_frame = frame; - gspca_dev->fr_q = (i + 1) % gspca_dev->nframes; - PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - gspca_dev->fr_o); + atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; @@ -2088,7 +1975,7 @@ static int read_alloc(struct gspca_dev *gspca_dev, static unsigned int dev_poll(struct file *file, poll_table *wait) { struct gspca_dev *gspca_dev = file->private_data; - int i, ret; + int ret; PDEBUG(D_FRAM, "poll"); @@ -2106,11 +1993,9 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) return POLLERR; - /* check the next incoming buffer */ - i = gspca_dev->fr_o; - i = gspca_dev->fr_queue[i]; - if (gspca_dev->frame[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE) - ret = POLLIN | POLLRDNORM; /* something to read */ + /* check if an image has been received */ + if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) + ret = POLLIN | POLLRDNORM; /* yes */ else ret = 0; mutex_unlock(&gspca_dev->queue_lock); @@ -2214,9 +2099,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_enumaudio = vidioc_enumaudio, .vidioc_querymenu = vidioc_querymenu, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, @@ -2235,9 +2117,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_register = vidioc_s_register, #endif .vidioc_g_chip_ident = vidioc_g_chip_ident, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, -#endif }; static struct video_device gspca_template = { @@ -2253,31 +2132,18 @@ static struct video_device gspca_template = { * This function must be called by the sub-driver when it is * called for probing a new device. */ -int gspca_dev_probe(struct usb_interface *intf, +int gspca_dev_probe2(struct usb_interface *intf, const struct usb_device_id *id, const struct sd_desc *sd_desc, int dev_size, struct module *module) { - struct usb_interface_descriptor *interface; struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); int ret; PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); - /* we don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) { - PDEBUG(D_ERR, "Too many config"); - return -ENODEV; - } - - /* the USB video interface must be the first one */ - interface = &intf->cur_altsetting->desc; - if (dev->config->desc.bNumInterfaces != 1 && - interface->bInterfaceNumber != 0) - return -ENODEV; - /* create the device */ if (dev_size < sizeof *gspca_dev) dev_size = sizeof *gspca_dev; @@ -2293,8 +2159,26 @@ int gspca_dev_probe(struct usb_interface *intf, goto out; } gspca_dev->dev = dev; - gspca_dev->iface = interface->bInterfaceNumber; + gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; gspca_dev->nbalt = intf->num_altsetting; + + /* check if any audio device */ + if (dev->config->desc.bNumInterfaces != 1) { + int i; + struct usb_interface *intf2; + + for (i = 0; i < dev->config->desc.bNumInterfaces; i++) { + intf2 = dev->config->interface[i]; + if (intf2 != NULL + && intf2->altsetting != NULL + && intf2->altsetting->desc.bInterfaceClass == + USB_CLASS_AUDIO) { + gspca_dev->audio = 1; + break; + } + } + } + gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ @@ -2345,6 +2229,31 @@ out: kfree(gspca_dev); return ret; } +EXPORT_SYMBOL(gspca_dev_probe2); + +/* same function as the previous one, but check the interface */ +int gspca_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module) +{ + struct usb_device *dev = interface_to_usbdev(intf); + + /* we don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) { + PDEBUG(D_ERR, "%04x:%04x too many config", + id->idVendor, id->idProduct); + return -ENODEV; + } + + /* the USB video interface must be the first one */ + if (dev->config->desc.bNumInterfaces != 1 + && intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + + return gspca_dev_probe2(intf, id, sd_desc, dev_size, module); +} EXPORT_SYMBOL(gspca_dev_probe); /* diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 8b963dfae86..b749c36d9f7 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -7,7 +7,6 @@ #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/mutex.h> -#include <linux/slab.h> /* compilation option */ #define GSPCA_DEBUG 1 @@ -148,7 +147,6 @@ enum gspca_packet_type { struct gspca_frame { __u8 *data; /* frame buffer */ - __u8 *data_end; /* end of frame while filling */ int vma_use_count; struct v4l2_buffer v4l2_buf; }; @@ -177,13 +175,14 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; - struct gspca_frame *cur_frame; /* frame beeing filled */ + u8 *image; /* image beeing filled */ __u32 frsz; /* frame size */ - char nframes; /* number of frames */ - char fr_i; /* frame being filled */ - char fr_q; /* next frame to queue */ - char fr_o; /* next frame to dequeue */ + u32 image_len; /* current length of image */ + atomic_t fr_q; /* next frame to queue */ + atomic_t fr_i; /* frame being filled */ signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ + char nframes; /* number of frames */ + u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ __u8 streaming; @@ -199,6 +198,7 @@ struct gspca_dev { struct mutex read_lock; /* read protection */ struct mutex queue_lock; /* ISOC queue protection */ int usb_err; /* USB error - protected by usb_lock */ + u16 pkt_size; /* ISOC packet size */ #ifdef CONFIG_PM char frozen; /* suspend - resume */ #endif @@ -209,7 +209,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ - u16 pkt_size; /* ISOC packet size */ + u8 audio; /* presence of audio device */ }; int gspca_dev_probe(struct usb_interface *intf, @@ -217,12 +217,16 @@ int gspca_dev_probe(struct usb_interface *intf, const struct sd_desc *sd_desc, int dev_size, struct module *module); +int gspca_dev_probe2(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module); void gspca_disconnect(struct usb_interface *intf); void gspca_frame_add(struct gspca_dev *gspca_dev, enum gspca_packet_type packet_type, const u8 *data, int len); -struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev); #ifdef CONFIG_PM int gspca_suspend(struct usb_interface *intf, pm_message_t message); int gspca_resume(struct usb_interface *intf); diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index 84ecd56c647..12d9cf4caba 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -50,7 +50,7 @@ struct sd { struct workqueue_struct *work_thread; u8 quality; /* image quality */ u8 jpegqual; /* webcam quality */ - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; struct jlj_command { @@ -282,7 +282,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) destroy_workqueue(dev->work_thread); dev->work_thread = NULL; mutex_lock(&gspca_dev->usb_lock); - kfree(dev->jpeg_hdr); } /* this function is called at probe and resume time */ @@ -298,9 +297,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret; /* create the JPEG header */ - dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (dev->jpeg_hdr == NULL) - return -ENOMEM; jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(dev->jpeg_hdr, dev->quality); diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h index 1127a405c9b..51af3ee3ab8 100644 --- a/drivers/media/video/gspca/m5602/m5602_bridge.h +++ b/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -19,6 +19,7 @@ #ifndef M5602_BRIDGE_H_ #define M5602_BRIDGE_H_ +#include <linux/slab.h> #include "gspca.h" #define MODULE_NAME "ALi m5602" diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 4294c75e3b1..b073d66acd0 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -305,30 +305,23 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, sd->frame_count); } else { - struct gspca_frame *frame; int cur_frame_len; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - cur_frame_len = frame->data_end - frame->data; + cur_frame_len = gspca_dev->image_len; /* Remove urb header */ data += 4; len -= 4; - if (cur_frame_len + len <= frame->v4l2_buf.length) { + if (cur_frame_len + len <= gspca_dev->frsz) { PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", sd->frame_count, len); gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - } else if (frame->v4l2_buf.length - cur_frame_len > 0) { + } else { /* Add the remaining data up to frame size */ gspca_frame_add(gspca_dev, INTER_PACKET, data, - frame->v4l2_buf.length - cur_frame_len); + gspca_dev->frsz - cur_frame_len); } } } diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 6b3be4fa2c0..fbd91545497 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -17,7 +17,6 @@ */ #include <linux/kthread.h> -#include <linux/slab.h> #include "m5602_s5k83a.h" static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 3d9229e22b2..031f7195ce0 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -41,7 +41,7 @@ struct sd { #define QUALITY_MAX 70 #define QUALITY_DEF 50 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -200,9 +200,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int i; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -317,13 +314,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_ERR, "Camera Stop failed"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -486,7 +476,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index f36e11a0458..2b2cbdbf03f 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -41,6 +41,11 @@ #include <linux/input.h> #include "gspca.h" +/* The jpeg_hdr is used by w996Xcf only */ +/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ +#define CONEX_CAM +#include "jpeg.h" + MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("OV519 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -90,6 +95,7 @@ struct sd { #define QUALITY_DEF 50 __u8 stopped; /* Streaming is temporarily paused */ + __u8 first_frame; __u8 frame_rate; /* current Framerate */ __u8 clockdiv; /* clockdiv override */ @@ -115,7 +121,7 @@ struct sd { int sensor_height; int sensor_reg_cache[256]; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* Note this is a bit of a hack, but the w9968cf driver needs the code for all @@ -3147,7 +3153,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autobrightness = AUTOBRIGHT_DEF; if (sd->sensor == SEN_OV7670) { sd->freq = OV7670_FREQ_DEF; - gspca_dev->ctrl_dis = 1 << FREQ_IDX; + gspca_dev->ctrl_dis = (1 << FREQ_IDX) | (1 << COLOR_IDX); } else { sd->freq = FREQ_DEF; gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | @@ -3961,6 +3967,8 @@ static int sd_start(struct gspca_dev *gspca_dev) sd_reset_snapshot(gspca_dev); sd->snapshot_pressed = 0; + sd->first_frame = 3; + ret = ov51x_restart(sd); if (ret < 0) goto out; @@ -4153,13 +4161,23 @@ static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + /* A short read signals EOF */ if (len < OVFX2_BULK_SIZE) { - gspca_frame_add(gspca_dev, LAST_PACKET, data, len); + /* If the frame is short, and it is one of the first ones + the sensor and bridge are still syncing, so drop it. */ + if (sd->first_frame) { + sd->first_frame--; + if (gspca_dev->image_len < + sd->gspca_dev.width * sd->gspca_dev.height) + gspca_dev->last_packet_type = DISCARD_PACKET; + } + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - return; } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index dc1e4efe30f..96cb3a97658 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -987,13 +987,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data + 12, len - 12); /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { - struct gspca_frame *frame; - sd->last_pts = 0; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) - goto discard; - if (frame->data_end - frame->data + (len - 12) != + if (gspca_dev->image_len + len - 12 != gspca_dev->width * gspca_dev->height * 2) { PDEBUG(D_PACK, "wrong sized frame"); goto discard; diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 2a68220d1ad..a66df07d762 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -402,7 +402,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), - 1, /* request */ + 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, @@ -804,7 +804,6 @@ static const unsigned char pac_jpeg_header2[] = { }; static void pac_start_frame(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, __u16 lines, __u16 samples_per_line) { unsigned char tmpbuf[4]; @@ -829,19 +828,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - struct gspca_frame *frame; + u8 *image; unsigned char *sof; sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n, lum_offset, footer_length; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* 6 bytes after the FF D9 EOF marker a number of lumination bytes are send corresponding to different parts of the image, the 14th and 15th byte after the EOF seem to @@ -852,16 +845,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { - frame->data_end += n; + gspca_dev->image_len += n; n = 0; + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); } - gspca_frame_add(gspca_dev, INTER_PACKET, - data, n); - if (gspca_dev->last_packet_type != DISCARD_PACKET && - frame->data_end[-2] == 0xff && - frame->data_end[-1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); + + image = gspca_dev->image; + if (image != NULL + && image[gspca_dev->image_len - 2] == 0xff + && image[gspca_dev->image_len - 1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; len -= n; @@ -877,7 +871,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Start the new frame with the jpeg header */ /* The PAC7302 has the image rotated 90 degrees */ - pac_start_frame(gspca_dev, frame, + pac_start_frame(gspca_dev, gspca_dev->width, gspca_dev->height); } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); @@ -1200,6 +1194,7 @@ static const struct usb_device_id device_table[] __devinitconst = { {USB_DEVICE(0x093a, 0x2621)}, {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x2625)}, {USB_DEVICE(0x093a, 0x2626)}, {USB_DEVICE(0x093a, 0x2628)}, {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 44fed968672..1cb7e99e92b 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -270,7 +270,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), - 1, /* request */ + 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, @@ -599,7 +599,6 @@ static const unsigned char pac_jpeg_header2[] = { }; static void pac_start_frame(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, __u16 lines, __u16 samples_per_line) { unsigned char tmpbuf[4]; @@ -624,19 +623,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; + u8 *image; unsigned char *sof; - struct gspca_frame *frame; sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n, lum_offset, footer_length; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* 6 bytes after the FF D9 EOF marker a number of lumination bytes are send corresponding to different parts of the image, the 14th and 15th byte after the EOF seem to @@ -647,16 +640,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { - frame->data_end += n; + gspca_dev->image_len += n; n = 0; + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); } - gspca_frame_add(gspca_dev, INTER_PACKET, - data, n); - if (gspca_dev->last_packet_type != DISCARD_PACKET && - frame->data_end[-2] == 0xff && - frame->data_end[-1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); + image = gspca_dev->image; + if (image != NULL + && image[gspca_dev->image_len - 2] == 0xff + && image[gspca_dev->image_len - 1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; len -= n; @@ -671,7 +664,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ - pac_start_frame(gspca_dev, frame, + pac_start_frame(gspca_dev, gspca_dev->height, gspca_dev->width); } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 644a7fd4701..83a718f0f3f 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -20,7 +20,6 @@ #ifdef CONFIG_INPUT #include <linux/input.h> -#include <linux/slab.h> #endif #include "gspca.h" @@ -89,7 +88,7 @@ struct sd { u8 hstart; u8 vstart; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; u8 quality; u8 flags; @@ -2162,10 +2161,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int height = gspca_dev->height; u8 fmt, scale = 0; - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (sd->jpeg_hdr == NULL) - return -ENOMEM; - jpeg_define(sd->jpeg_hdr, height, width, 0x21); jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -2197,8 +2192,8 @@ static int sd_start(struct gspca_dev *gspca_dev) } configure_sensor_output(gspca_dev, mode); - reg_w(gspca_dev, 0x1100, sd->jpeg_hdr + JPEG_QT0_OFFSET, 64); - reg_w(gspca_dev, 0x1140, sd->jpeg_hdr + JPEG_QT1_OFFSET, 64); + reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); reg_w1(gspca_dev, 0x1189, scale); @@ -2226,12 +2221,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - kfree(sd->jpeg_hdr); -} - static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; @@ -2397,7 +2386,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, #ifdef CONFIG_INPUT .int_pkt_scan = sd_int_pkt_scan, diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 95354a339e3..204bb3af455 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1251,16 +1251,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { /* In raw mode we sometimes get some garbage after the frame ignore this */ - struct gspca_frame *frame; int used; int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - used = frame->data_end - frame->data; + used = gspca_dev->image_len; if (used + len > size) len = size - used; } diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 176c5b3d5e6..370544361be 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -22,7 +22,6 @@ #define MODULE_NAME "sonixj" #include <linux/input.h> -#include <linux/slab.h> #include "gspca.h" #include "jpeg.h" @@ -67,7 +66,11 @@ struct sd { #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 u8 sensor; /* Type of image sensor chip */ -enum { + u8 i2c_addr; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; +enum sensors { SENSOR_ADCM1700, SENSOR_GC0307, SENSOR_HV7131R, @@ -82,10 +85,6 @@ enum { SENSOR_PO2030N, SENSOR_SOI768, SENSOR_SP80708, -} sensors; - u8 i2c_addr; - - u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -392,7 +391,7 @@ static const u8 sn_gc0307[0x1c] = { static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -403,7 +402,7 @@ static const u8 sn_hv7131[0x1c] = { static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -1644,6 +1643,7 @@ static void bridge_init(struct gspca_dev *gspca_dev, const u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; + u8 reg0102[2]; const u8 *reg9a; static const u8 reg9a_def[] = {0x00, 0x40, 0x20, 0x00, 0x00, 0x00}; @@ -1656,7 +1656,11 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, sn9c1xx[1]); /* configure gpio */ - reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); + reg0102[0] = sn9c1xx[1]; + reg0102[1] = sn9c1xx[2]; + if (gspca_dev->audio) + reg0102[1] |= 0x04; /* keep the audio connection */ + reg_w(gspca_dev, 0x01, reg0102, 2); reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { @@ -1737,13 +1741,12 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x40); break; case SENSOR_PO2030N: + case SENSOR_OV7660: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); break; - case SENSOR_OV7660: - /* fall thru */ case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); @@ -1816,7 +1819,7 @@ static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; const u8 *sn9c1xx; - u8 regGpio[] = { 0x29, 0x74 }; + u8 regGpio[] = { 0x29, 0x74 }; /* with audio */ u8 regF1; /* setup a selector by bridge */ @@ -1856,7 +1859,7 @@ static int sd_init(struct gspca_dev *gspca_dev) po2030n_probe(gspca_dev); break; } - regGpio[1] = 0x70; + regGpio[1] = 0x70; /* no audio */ reg_w(gspca_dev, 0x01, regGpio, 2); break; default: @@ -2274,7 +2277,7 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg2, reg17; + u8 reg1, reg17; const u8 *sn9c1xx; const u8 (*init)[8]; int mode; @@ -2304,23 +2307,6 @@ static int sd_start(struct gspca_dev *gspca_dev) /* initialize the sensor */ i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); - switch (sd->sensor) { - case SENSOR_ADCM1700: - reg2 = 0x60; - break; - case SENSOR_OM6802: - reg2 = 0x71; - break; - case SENSOR_SP80708: - reg2 = 0x62; - break; - default: - reg2 = 0x40; - break; - } - reg_w1(gspca_dev, 0x02, reg2); - reg_w1(gspca_dev, 0x02, reg2); - reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c new file mode 100644 index 00000000000..3f514eb1d99 --- /dev/null +++ b/drivers/media/video/gspca/spca1528.c @@ -0,0 +1,605 @@ +/* + * spca1528 subdriver + * + * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca1528" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_DESCRIPTION("SPCA1528 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 brightness; + u8 contrast; + u8 hue; + u8 color; + u8 sharpness; + + u8 pkt_seq; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); + +static const struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 8, + .step = 1, +#define CONTRAST_DEF 1 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, +#define HUE_DEF 0 + .default_value = HUE_DEF, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 8, + .step = 1, +#define COLOR_DEF 1 + .default_value = COLOR_DEF, + }, + .set = sd_setcolor, + .get = sd_getcolor, + }, + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define SHARPNESS_DEF 0 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { +/* (does not work correctly) + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 5 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, +*/ + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +/* read <len> bytes to gspca usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + u8 req, + u16 index, + int len) +{ +#if USB_BUF_SZ < 64 +#error "USB buffer too small" +#endif + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0000, /* value */ + index, + gspca_dev->usb_buf, len, + 500); + PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, + gspca_dev->usb_buf[0]); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r err %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_wb(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index, + u8 byte) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte); + gspca_dev->usb_buf[0] = byte; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void wait_status_0(struct gspca_dev *gspca_dev) +{ + int i; + + i = 20; + do { + reg_r(gspca_dev, 0x21, 0x0000, 1); + if (gspca_dev->usb_buf[0] == 0) + return; + msleep(30); + } while (--i > 0); + PDEBUG(D_ERR, "wait_status_0 timeout"); + gspca_dev->usb_err = -ETIME; +} + +static void wait_status_1(struct gspca_dev *gspca_dev) +{ + int i; + + i = 10; + do { + reg_r(gspca_dev, 0x21, 0x0001, 1); + msleep(10); + if (gspca_dev->usb_buf[0] == 1) { + reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00); + reg_r(gspca_dev, 0x21, 0x0001, 1); + return; + } + } while (--i > 0); + PDEBUG(D_ERR, "wait_status_1 timeout"); + gspca_dev->usb_err = -ETIME; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue); +} + +static void setcolor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color); +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */ + /*fixme: 256 in ms-win traces*/ + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->hue = HUE_DEF; + sd->color = COLOR_DEF; + sd->sharpness = SHARPNESS_DEF; + + gspca_dev->nbalt = 4; /* use alternate setting 3 */ + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0x00, 0x0001, 0x2067); + reg_w(gspca_dev, 0x00, 0x00d0, 0x206b); + reg_w(gspca_dev, 0x00, 0x0000, 0x206c); + reg_w(gspca_dev, 0x00, 0x0001, 0x2069); + msleep(8); + reg_w(gspca_dev, 0x00, 0x00c0, 0x206b); + reg_w(gspca_dev, 0x00, 0x0000, 0x206c); + reg_w(gspca_dev, 0x00, 0x0001, 0x2069); + + reg_r(gspca_dev, 0x20, 0x0000, 1); + reg_r(gspca_dev, 0x20, 0x0000, 5); + reg_r(gspca_dev, 0x23, 0x0000, 64); + PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c], + &gspca_dev->usb_buf[0x30]); + reg_r(gspca_dev, 0x23, 0x0001, 64); + return gspca_dev->usb_err; +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + u8 mode; + + reg_r(gspca_dev, 0x00, 0x2520, 1); + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0xc5, 0x0003, 0x0000); + wait_status_1(gspca_dev); + + wait_status_0(gspca_dev); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode); + reg_r(gspca_dev, 0x25, 0x0004, 1); + reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); + reg_r(gspca_dev, 0x27, 0x0000, 1); + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialize the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + + /* the JPEG quality seems to be 82% */ + jpeg_set_qual(sd->jpeg_hdr, 82); + + /* set the controls */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + sethue(gspca_dev); + setcolor(gspca_dev); + setsharpness(gspca_dev); + + msleep(5); + reg_r(gspca_dev, 0x00, 0x2520, 1); + msleep(8); + + /* start the capture */ + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0x31, 0x0000, 0x0004); + wait_status_1(gspca_dev); + wait_status_0(gspca_dev); + msleep(200); + + sd->pkt_seq = 0; + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* stop the capture */ + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0x31, 0x0000, 0x0000); + wait_status_1(gspca_dev); + wait_status_0(gspca_dev); +} + +/* move a packet adding 0x00 after 0xff */ +static void add_packet(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + int i; + + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + } while (++i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 ffd9[] = {0xff, 0xd9}; + + /* image packets start with: + * 02 8n + * with <n> bit: + * 0x01: even (0) / odd (1) image + * 0x02: end of image when set + */ + if (len < 3) + return; /* empty packet */ + if (*data == 0x02) { + if (data[1] & 0x02) { + sd->pkt_seq = !(data[1] & 1); + add_packet(gspca_dev, data + 2, len - 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + return; + } + if ((data[1] & 1) != sd->pkt_seq) + goto err; + if (gspca_dev->last_packet_type == LAST_PACKET) + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + add_packet(gspca_dev, data + 2, len - 2); + return; + } +err: + gspca_dev->last_packet_type = DISCARD_PACKET; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hue; + return 0; +} + +static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->color = val; + if (gspca_dev->streaming) + setcolor(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->color; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x04fc, 0x1528)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + /* the video interface for isochronous transfer is 1 */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 1) + return -ENODEV; + + return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index b866c73c97d..c02beb6c1e9 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -57,7 +57,7 @@ struct sd { #define PalmPixDC85 13 #define ToptroIndus 14 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -669,9 +669,6 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 xmult, ymult; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -891,13 +888,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -1055,7 +1045,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c new file mode 100644 index 00000000000..7ae6522d4ed --- /dev/null +++ b/drivers/media/video/gspca/sq930x.c @@ -0,0 +1,1203 @@ +/* + * SQ930x subdriver + * + * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr> + * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl> + * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sq930x" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n" + "Gerard Klaver <gerard at gkall dot hobby dot nl\n" + "Sam Revitch <samr7@cs.washington.edu>"); +MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u16 expo; + u8 gain; + + u8 do_ctrl; + u8 gpio[2]; + u8 sensor; + u8 type; +#define Generic 0 +#define Creative_live_motion 1 +}; +enum sensors { + SENSOR_ICX098BQ, + SENSOR_LZ24BP, + SENSOR_MI0360, + SENSOR_MT9V111, /* = MI360SOC */ + SENSOR_OV7660, + SENSOR_OV9630, +}; + +static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); + +static const struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0x0001, + .maximum = 0x0fff, + .step = 1, +#define EXPO_DEF 0x0356 + .default_value = EXPO_DEF, + }, + .set = sd_setexpo, + .get = sd_getexpo, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0x01, + .maximum = 0xff, + .step = 1, +#define GAIN_DEF 0x8d + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, +}; + +/* sq930x registers */ +#define SQ930_CTRL_UCBUS_IO 0x0001 +#define SQ930_CTRL_I2C_IO 0x0002 +#define SQ930_CTRL_GPIO 0x0005 +#define SQ930_CTRL_CAP_START 0x0010 +#define SQ930_CTRL_CAP_STOP 0x0011 +#define SQ930_CTRL_SET_EXPOSURE 0x001d +#define SQ930_CTRL_RESET 0x001e +#define SQ930_CTRL_GET_DEV_INFO 0x001f + +/* gpio 1 (8..15) */ +#define SQ930_GPIO_DFL_I2C_SDA 0x0001 +#define SQ930_GPIO_DFL_I2C_SCL 0x0002 +#define SQ930_GPIO_RSTBAR 0x0004 +#define SQ930_GPIO_EXTRA1 0x0040 +#define SQ930_GPIO_EXTRA2 0x0080 +/* gpio 3 (24..31) */ +#define SQ930_GPIO_POWER 0x0200 +#define SQ930_GPIO_DFL_LED 0x1000 + +struct ucbus_write_cmd { + u16 bw_addr; + u8 bw_data; +}; +struct i2c_write_cmd { + u8 reg; + u16 val; +}; + +static const struct ucbus_write_cmd icx098bq_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce}, + {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e}, + {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02}, + {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02}, + {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00}, + {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04}, + {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00}, + {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48}, + {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, + {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, + {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff}, + {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, + {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00}, + {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, + {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, + {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c}, + {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30}, + {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30}, + {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc}, + {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, + {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00}, + {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00}, + {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa}, + {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa}, + {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, + {0xf800, 0x03} +}; +static const struct ucbus_write_cmd icx098bq_start_1[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xc0}, + {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xc0}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd icx098bq_start_2[] = { + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03} +}; + +static const struct ucbus_write_cmd lz24bp_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe}, + {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06}, + {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02}, + {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00}, + {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00}, + {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03}, + {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00}, + {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48}, + {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, + {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, + {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0}, + {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, + {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00}, + {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, + {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, + {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30}, + {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c}, + {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c}, + {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d}, + {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, + {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d}, + {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d}, + {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04}, + {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04}, + {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, + {0xf800, 0x03} +}; +static const struct ucbus_write_cmd lz24bp_start_1_gen[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xb3}, + {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xb3}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd lz24bp_start_1_clm[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, + {0xf5f4, 0xc0}, + {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, + {0xf5f4, 0xc0}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd lz24bp_start_2[] = { + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03} +}; + +static const struct ucbus_write_cmd mi0360_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc}, + {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00} +}; +static const struct i2c_write_cmd mi0360_init_23[] = { + {0x30, 0x0040}, /* reserved - def 0x0005 */ + {0x31, 0x0000}, /* reserved - def 0x002a */ + {0x34, 0x0100}, /* reserved - def 0x0100 */ + {0x3d, 0x068f}, /* reserved - def 0x068f */ +}; +static const struct i2c_write_cmd mi0360_init_24[] = { + {0x03, 0x01e5}, /* window height */ + {0x04, 0x0285}, /* window width */ +}; +static const struct i2c_write_cmd mi0360_init_25[] = { + {0x35, 0x0020}, /* global gain */ + {0x2b, 0x0020}, /* green1 gain */ + {0x2c, 0x002a}, /* blue gain */ + {0x2d, 0x0028}, /* red gain */ + {0x2e, 0x0020}, /* green2 gain */ +}; +static const struct ucbus_write_cmd mi0360_start_1[] = { + {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xa6}, + {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xa6}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; +static const struct i2c_write_cmd mi0360_start_2[] = { + {0x62, 0x041d}, /* reserved - def 0x0418 */ +}; +static const struct i2c_write_cmd mi0360_start_3[] = { + {0x05, 0x007b}, /* horiz blanking */ +}; +static const struct i2c_write_cmd mi0360_start_4[] = { + {0x05, 0x03f5}, /* horiz blanking */ +}; + +static const struct i2c_write_cmd mt9v111_init_0[] = { + {0x01, 0x0001}, /* select IFP/SOC registers */ + {0x06, 0x300c}, /* operating mode control */ + {0x08, 0xcc00}, /* output format control (RGB) */ + {0x01, 0x0004}, /* select sensor core registers */ +}; +static const struct i2c_write_cmd mt9v111_init_1[] = { + {0x03, 0x01e5}, /* window height */ + {0x04, 0x0285}, /* window width */ +}; +static const struct i2c_write_cmd mt9v111_init_2[] = { + {0x30, 0x7800}, + {0x31, 0x0000}, + {0x07, 0x3002}, /* output control */ + {0x35, 0x0020}, /* global gain */ + {0x2b, 0x0020}, /* green1 gain */ + {0x2c, 0x0020}, /* blue gain */ + {0x2d, 0x0020}, /* red gain */ + {0x2e, 0x0020}, /* green2 gain */ +}; +static const struct ucbus_write_cmd mt9v111_start_1[] = { + {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xaa}, + {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xaa}, + {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a}, + {0xf5f9, 0x0a} +}; +static const struct i2c_write_cmd mt9v111_init_3[] = { + {0x62, 0x0405}, +}; +static const struct i2c_write_cmd mt9v111_init_4[] = { +/* {0x05, 0x00ce}, */ + {0x05, 0x005d}, /* horizontal blanking */ +}; + +static const struct ucbus_write_cmd ov7660_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0}, + {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03} +}; + +static const struct ucbus_write_cmd ov9630_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00}, + {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03} +}; + +/* start parameters indexed by [sensor][mode] */ +static const struct cap_s { + u8 cc_sizeid; + u8 cc_bytes[32]; +} capconfig[4][2] = { + [SENSOR_ICX098BQ] = { + {2, /* Bayer 320x240 */ + {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, + [SENSOR_LZ24BP] = { + {2, /* Bayer 320x240 */ + {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, + [SENSOR_MI0360] = { + {2, /* Bayer 320x240 */ + {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, + [SENSOR_MT9V111] = { + {2, /* Bayer 320x240 */ + {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + {4, /* Bayer 640x480 */ + {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, + }, +}; + +struct sensor_s { + const char *name; + u8 i2c_addr; + u8 i2c_dum; + u8 gpio[5]; + u8 cmd_len; + const struct ucbus_write_cmd *cmd; +}; + +static const struct sensor_s sensor_tb[] = { + [SENSOR_ICX098BQ] = { + "icx098bp", + 0x00, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 8, icx098bq_start_0 + }, + [SENSOR_LZ24BP] = { + "lz24bp", + 0x00, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 8, lz24bp_start_0 + }, + [SENSOR_MI0360] = { + "mi0360", + 0x5d, 0x80, + {SQ930_GPIO_RSTBAR, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + 0 + }, + 7, mi0360_start_0 + }, + [SENSOR_MT9V111] = { + "mt9v111", + 0x5c, 0x7f, + {SQ930_GPIO_RSTBAR, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + 0 + }, + 7, mi0360_start_0 + }, + [SENSOR_OV7660] = { + "ov7660", + 0x21, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 7, ov7660_start_0 + }, + [SENSOR_OV9630] = { + "ov9630", + 0x30, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 7, ov9630_start_0 + }, +}; + +static void reg_r(struct gspca_dev *gspca_dev, + u16 value, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x0c, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r %04x failed %d", value, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); + msleep(30); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w %04x %04x failed %d", value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index, + const u8 *data, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x", + value, index, *data, data[len - 1]); + memcpy(gspca_dev->usb_buf, data, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, gspca_dev->usb_buf, len, + 1000); + msleep(30); + if (ret < 0) { + PDEBUG(D_ERR, "reg_wb %04x %04x failed %d", value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void i2c_write(struct sd *sd, + const struct i2c_write_cmd *cmd, + int ncmds) +{ + struct gspca_dev *gspca_dev = &sd->gspca_dev; + const struct sensor_s *sensor; + u16 val, idx; + u8 *buf; + int ret; + + if (gspca_dev->usb_err < 0) + return; + + sensor = &sensor_tb[sd->sensor]; + + val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO; + idx = (cmd->val & 0xff00) | cmd->reg; + + buf = gspca_dev->usb_buf; + *buf++ = sensor->i2c_dum; + *buf++ = cmd->val; + + while (--ncmds > 0) { + cmd++; + *buf++ = cmd->reg; + *buf++ = cmd->val >> 8; + *buf++ = sensor->i2c_dum; + *buf++ = cmd->val; + } + + PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x", + val, idx, gspca_dev->usb_buf[0], buf[-1]); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, idx, + gspca_dev->usb_buf, buf - gspca_dev->usb_buf, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "i2c_write failed %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void ucbus_write(struct gspca_dev *gspca_dev, + const struct ucbus_write_cmd *cmd, + int ncmds, + int batchsize) +{ + u8 *buf; + u16 val, idx; + int len, ret; + + if (gspca_dev->usb_err < 0) + return; + +#ifdef GSPCA_DEBUG + if ((batchsize - 1) * 3 > USB_BUF_SZ) { + err("Bug: usb_buf overflow"); + gspca_dev->usb_err = -ENOMEM; + return; + } +#endif + + for (;;) { + len = ncmds; + if (len > batchsize) + len = batchsize; + ncmds -= len; + + val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO; + idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8); + + buf = gspca_dev->usb_buf; + while (--len > 0) { + cmd++; + *buf++ = cmd->bw_addr; + *buf++ = cmd->bw_addr >> 8; + *buf++ = cmd->bw_data; + } + if (buf != gspca_dev->usb_buf) + PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x", + val, idx, + gspca_dev->usb_buf[0], buf[-1]); + else + PDEBUG(D_USBO, "ucbus v: %04x i: %04x", + val, idx); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, idx, + gspca_dev->usb_buf, buf - gspca_dev->usb_buf, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "ucbus_write failed %d", ret); + gspca_dev->usb_err = ret; + return; + } + msleep(30); + if (ncmds <= 0) + break; + cmd++; + } +} + +static void gpio_set(struct sd *sd, u16 val, u16 mask) +{ + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + if (mask & 0x00ff) { + sd->gpio[0] &= ~mask; + sd->gpio[0] |= val; + reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO, + ~sd->gpio[0] << 8); + } + mask >>= 8; + val >>= 8; + if (mask) { + sd->gpio[1] &= ~mask; + sd->gpio[1] |= val; + reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO, + ~sd->gpio[1] << 8); + } +} + +static void gpio_init(struct sd *sd, + const u8 *gpio) +{ + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio, 0x000f); +} + +static void bridge_init(struct sd *sd) +{ + static const struct ucbus_write_cmd clkfreq_cmd = { + 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */ + }; + + ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1); + + gpio_set(sd, SQ930_GPIO_POWER, 0xff00); +} + +static void cmos_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + const struct sensor_s *sensor; + static const u8 probe_order[] = { +/* SENSOR_LZ24BP, (tested as ccd) */ + SENSOR_OV9630, + SENSOR_MI0360, + SENSOR_OV7660, + SENSOR_MT9V111, + }; + + for (i = 0; i < ARRAY_SIZE(probe_order); i++) { + sensor = &sensor_tb[probe_order[i]]; + ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8); + gpio_init(sd, sensor->gpio); + msleep(100); + reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1); + msleep(100); + if (gspca_dev->usb_buf[0] != 0) + break; + } + if (i >= ARRAY_SIZE(probe_order)) + PDEBUG(D_PROBE, "Unknown sensor"); + else + sd->sensor = probe_order[i]; +} + +static void mt9v111_init(struct gspca_dev *gspca_dev) +{ + int i, nwait; + static const u8 cmd_001b[] = { + 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00 + }; + static const u8 cmd_011b[][7] = { + {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00}, + {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00}, + {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00}, + {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00}, + }; + + reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b); + for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) { + reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i], + ARRAY_SIZE(cmd_011b[0])); + msleep(400); + nwait = 20; + for (;;) { + reg_r(gspca_dev, 0x031b, 1); + if (gspca_dev->usb_buf[0] == 0 + || gspca_dev->usb_err != 0) + break; + if (--nwait < 0) { + PDEBUG(D_PROBE, "mt9v111_init timeout"); + gspca_dev->usb_err = -ETIME; + return; + } + msleep(50); + } + } +} + +static void global_init(struct sd *sd, int first_time) +{ + switch (sd->sensor) { + case SENSOR_ICX098BQ: + if (first_time) + ucbus_write(&sd->gspca_dev, + icx098bq_start_0, + 8, 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + case SENSOR_LZ24BP: + if (sd->type != Creative_live_motion) + gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff); + else + gpio_set(sd, 0, 0x00ff); + msleep(50); + if (first_time) + ucbus_write(&sd->gspca_dev, + lz24bp_start_0, + 8, 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + case SENSOR_MI0360: + if (first_time) + ucbus_write(&sd->gspca_dev, + mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2); + break; + default: +/* case SENSOR_MT9V111: */ + if (first_time) + mt9v111_init(&sd->gspca_dev); + else + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + } +} + +static void lz24bp_ppl(struct sd *sd, u16 ppl) +{ + struct ucbus_write_cmd cmds[2] = { + {0xf810, ppl >> 8}, + {0xf811, ppl} + }; + + ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, integclks, intstartclk, frameclks, min_frclk; + const struct sensor_s *sensor; + u16 cmd; + u8 buf[15]; + + integclks = sd->expo; + i = 0; + cmd = SQ930_CTRL_SET_EXPOSURE; + + switch (sd->sensor) { + case SENSOR_ICX098BQ: /* ccd */ + case SENSOR_LZ24BP: + min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f; + if (integclks >= min_frclk) { + intstartclk = 0; + frameclks = integclks; + } else { + intstartclk = min_frclk - integclks; + frameclks = min_frclk; + } + buf[i++] = intstartclk >> 8; + buf[i++] = intstartclk; + buf[i++] = frameclks >> 8; + buf[i++] = frameclks; + buf[i++] = sd->gain; + break; + default: /* cmos */ +/* case SENSOR_MI0360: */ +/* case SENSOR_MT9V111: */ + cmd |= 0x0100; + sensor = &sensor_tb[sd->sensor]; + buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */ + buf[i++] = 0x08; /* 2 * ni2c */ + buf[i++] = 0x09; /* reg = shutter width */ + buf[i++] = integclks >> 8; /* val H */ + buf[i++] = sensor->i2c_dum; + buf[i++] = integclks; /* val L */ + buf[i++] = 0x35; /* reg = global gain */ + buf[i++] = 0x00; /* val H */ + buf[i++] = sensor->i2c_dum; + buf[i++] = 0x80 + sd->gain / 2; /* val L */ + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x83; + break; + } + reg_wb(gspca_dev, cmd, 0, buf, i); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + sd->sensor = id->driver_info >> 8; + sd->type = id->driver_info; + + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + cam->bulk = 1; + + sd->gain = GAIN_DEF; + sd->expo = EXPO_DEF; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */ + +/*fixme: is this needed for icx098bp and mi0360? + if (sd->sensor != SENSOR_LZ24BP) + reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000); + */ + + reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8); +/* it returns: + * 03 00 12 93 0b f6 c9 00 live! ultra + * 03 00 07 93 0b f6 ca 00 live! ultra for notebook + * 03 00 12 93 0b fe c8 00 Trust WB-3500T + * 02 00 06 93 0b fe c8 00 Joy-IT 318S + * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq + * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam + * + * byte + * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit) + * 1: 00 + * 2: 06 / 07 / 12 = mode webcam? firmware?? + * 3: 93 chip = 930b (930b or 930c) + * 4: 0b + * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors) + * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam? + * 7: 00 + */ + PDEBUG(D_PROBE, "info: %02x %02x %02x %02x %02x %02x %02x %02x", + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2], + gspca_dev->usb_buf[3], + gspca_dev->usb_buf[4], + gspca_dev->usb_buf[5], + gspca_dev->usb_buf[6], + gspca_dev->usb_buf[7]); + + bridge_init(sd); + + if (sd->sensor == SENSOR_MI0360) { + + /* no sensor probe for icam tracer */ + if (gspca_dev->usb_buf[5] == 0xf6) /* if CMOS */ + sd->sensor = SENSOR_ICX098BQ; + else + cmos_probe(gspca_dev); + } + + PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name); + + global_init(sd, 1); + return gspca_dev->usb_err; +} + +/* send the start/stop commands to the webcam */ +static void send_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const struct cap_s *cap; + int mode; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + cap = &capconfig[sd->sensor][mode]; + reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START, + 0x0a00 | cap->cc_sizeid, + cap->cc_bytes, 32); +} + +static void send_stop(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0); +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */ + sd->do_ctrl = 0; + gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8; + return 0; +} + +/* start the capture */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode; + + bridge_init(sd); + global_init(sd, 0); + msleep(100); + + switch (sd->sensor) { + case SENSOR_ICX098BQ: + ucbus_write(gspca_dev, icx098bq_start_0, + ARRAY_SIZE(icx098bq_start_0), + 8); + ucbus_write(gspca_dev, icx098bq_start_1, + ARRAY_SIZE(icx098bq_start_1), + 5); + ucbus_write(gspca_dev, icx098bq_start_2, + ARRAY_SIZE(icx098bq_start_2), + 6); + msleep(50); + + /* 1st start */ + send_start(gspca_dev); + gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); + msleep(70); + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000); + gpio_set(sd, 0x7f, 0x00ff); + + /* 2nd start */ + send_start(gspca_dev); + gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); + goto out; + case SENSOR_LZ24BP: + ucbus_write(gspca_dev, lz24bp_start_0, + ARRAY_SIZE(lz24bp_start_0), + 8); + if (sd->type != Creative_live_motion) + ucbus_write(gspca_dev, lz24bp_start_1_gen, + ARRAY_SIZE(lz24bp_start_1_gen), + 5); + else + ucbus_write(gspca_dev, lz24bp_start_1_clm, + ARRAY_SIZE(lz24bp_start_1_clm), + 5); + ucbus_write(gspca_dev, lz24bp_start_2, + ARRAY_SIZE(lz24bp_start_2), + 6); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310); + msleep(10); + break; + case SENSOR_MI0360: + ucbus_write(gspca_dev, mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + i2c_write(sd, mi0360_init_23, + ARRAY_SIZE(mi0360_init_23)); + i2c_write(sd, mi0360_init_24, + ARRAY_SIZE(mi0360_init_24)); + i2c_write(sd, mi0360_init_25, + ARRAY_SIZE(mi0360_init_25)); + ucbus_write(gspca_dev, mi0360_start_1, + ARRAY_SIZE(mi0360_start_1), + 5); + i2c_write(sd, mi0360_start_2, + ARRAY_SIZE(mi0360_start_2)); + i2c_write(sd, mi0360_start_3, + ARRAY_SIZE(mi0360_start_3)); + + /* 1st start */ + send_start(gspca_dev); + msleep(60); + send_stop(gspca_dev); + + i2c_write(sd, + mi0360_start_4, ARRAY_SIZE(mi0360_start_4)); + break; + default: +/* case SENSOR_MT9V111: */ + ucbus_write(gspca_dev, mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + i2c_write(sd, mt9v111_init_0, + ARRAY_SIZE(mt9v111_init_0)); + i2c_write(sd, mt9v111_init_1, + ARRAY_SIZE(mt9v111_init_1)); + i2c_write(sd, mt9v111_init_2, + ARRAY_SIZE(mt9v111_init_2)); + ucbus_write(gspca_dev, mt9v111_start_1, + ARRAY_SIZE(mt9v111_start_1), + 5); + i2c_write(sd, mt9v111_init_3, + ARRAY_SIZE(mt9v111_init_3)); + i2c_write(sd, mt9v111_init_4, + ARRAY_SIZE(mt9v111_init_4)); + break; + } + + send_start(gspca_dev); +out: + msleep(1000); + + if (sd->sensor == SENSOR_MT9V111) + gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED); + + sd->do_ctrl = 1; /* set the exposure */ + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_MT9V111) + gpio_set(sd, 0, SQ930_GPIO_DFL_LED); + send_stop(gspca_dev); +} + +/* function called when the application gets a new frame */ +/* It sets the exposure if required and restart the bulk transfer. */ +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0) + return; + sd->do_ctrl = 0; + + setexposure(gspca_dev); + + gspca_dev->cam.bulk_nurbs = 1; + ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC); + if (ret < 0) + PDEBUG(D_ERR|D_PACK, "sd_dq_callback() err %d", ret); + + /* wait a little time, otherwise the webcam crashes */ + msleep(100); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->do_ctrl) + gspca_dev->cam.bulk_nurbs = 0; + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + sd->do_ctrl = 1; + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} +static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->expo = val; + if (gspca_dev->streaming) + sd->do_ctrl = 1; + return 0; +} + +static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->expo; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_dq_callback, +}; + +/* Table of supported USB devices */ +#define ST(sensor, type) \ + .driver_info = (SENSOR_ ## sensor << 8) \ + | (type) +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)}, + {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)}, + {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)}, + {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)}, + {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)}, + {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 0fb534210a2..2aedf4b1bfa 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -36,11 +36,11 @@ struct sd { unsigned char colors; unsigned char lightfreq; u8 quality; -#define QUALITY_MIN 60 +#define QUALITY_MIN 70 #define QUALITY_MAX 95 -#define QUALITY_DEF 80 +#define QUALITY_DEF 88 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -337,9 +337,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret, value; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -412,13 +409,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -578,7 +568,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index 992ce530f13..053a27e3a40 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -30,6 +30,7 @@ #ifndef STV06XX_H_ #define STV06XX_H_ +#include <linux/slab.h> #include "gspca.h" #define MODULE_NAME "STV06xx" diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 0c786e00ebc..9494f86b9a8 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -54,7 +54,7 @@ struct sd { #define MegapixV4 4 #define MegaImageVI 5 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -805,7 +805,7 @@ static int sd_init(struct gspca_dev *gspca_dev) /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, 8, 3, 0x9e, 1); - /* Twice sequencial need status 0xff->0x9e->0x9d */ + /* Twice sequential need status 0xff->0x9e->0x9d */ spca504A_acknowledged_command(gspca_dev, 0x24, 8, 3, 0x9e, 0); @@ -842,9 +842,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int enable; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -880,7 +877,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, 8, 3, 0x9e, 1); - /* Twice sequencial need status 0xff->0x9e->0x9d */ + /* Twice sequential need status 0xff->0x9e->0x9d */ spca504A_acknowledged_command(gspca_dev, 0x24, 8, 3, 0x9e, 0); spca504A_acknowledged_command(gspca_dev, 0x24, @@ -954,13 +951,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -1162,7 +1152,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 63014372adb..3b3b983f2b9 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -1,5 +1,7 @@ /* - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * T613 subdriver + * + * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +28,7 @@ #define MODULE_NAME "t613" +#include <linux/slab.h> #include "gspca.h" #define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0) @@ -44,18 +47,20 @@ struct sd { u8 gamma; u8 sharpness; u8 freq; - u8 red_balance; /* split balance */ - u8 blue_balance; - u8 global_gain; /* aka gain */ - u8 whitebalance; /* set default r/g/b and activate */ + u8 red_gain; + u8 blue_gain; + u8 green_gain; + u8 awb; /* set default r/g/b and activate */ u8 mirror; u8 effect; u8 sensor; -#define SENSOR_OM6802 0 -#define SENSOR_OTHER 1 -#define SENSOR_TAS5130A 2 -#define SENSOR_LT168G 3 /* must verify if this is the actual model */ +}; +enum sensors { + SENSOR_OM6802, + SENSOR_OTHER, + SENSOR_TAS5130A, + SENSOR_LT168G, /* must verify if this is the actual model */ }; /* V4L2 controls supported by the driver */ @@ -74,24 +79,22 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); - -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val); - -static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); + +static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val); static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu); - static const struct ctrl sd_ctrls[] = { { { @@ -177,8 +180,8 @@ static const struct ctrl sd_ctrls[] = { #define MIRROR_DEF 0 .default_value = MIRROR_DEF, }, - .set = sd_setflip, - .get = sd_getflip + .set = sd_setmirror, + .get = sd_getmirror }, { { @@ -198,15 +201,15 @@ static const struct ctrl sd_ctrls[] = { { .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "White Balance", + .name = "Auto White Balance", .minimum = 0, .maximum = 1, .step = 1, -#define WHITE_BALANCE_DEF 0 - .default_value = WHITE_BALANCE_DEF, +#define AWB_DEF 0 + .default_value = AWB_DEF, }, - .set = sd_setwhitebalance, - .get = sd_getwhitebalance + .set = sd_setawb, + .get = sd_getawb }, { { @@ -244,11 +247,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0x10, .maximum = 0x40, .step = 1, -#define BLUE_BALANCE_DEF 0x20 - .default_value = BLUE_BALANCE_DEF, +#define BLUE_GAIN_DEF 0x20 + .default_value = BLUE_GAIN_DEF, }, - .set = sd_setblue_balance, - .get = sd_getblue_balance, + .set = sd_setblue_gain, + .get = sd_getblue_gain, }, { { @@ -258,11 +261,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0x10, .maximum = 0x40, .step = 1, -#define RED_BALANCE_DEF 0x20 - .default_value = RED_BALANCE_DEF, +#define RED_GAIN_DEF 0x20 + .default_value = RED_GAIN_DEF, }, - .set = sd_setred_balance, - .get = sd_getred_balance, + .set = sd_setred_gain, + .get = sd_getred_gain, }, { { @@ -272,24 +275,14 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0x10, .maximum = 0x40, .step = 1, -#define global_gain_DEF 0x20 - .default_value = global_gain_DEF, +#define GAIN_DEF 0x20 + .default_value = GAIN_DEF, }, - .set = sd_setglobal_gain, - .get = sd_getglobal_gain, + .set = sd_setgain, + .get = sd_getgain, }, }; -static char *effects_control[] = { - "Normal", - "Emboss", /* disabled */ - "Monochrome", - "Sepia", - "Sketch", - "Sun Effect", /* disabled */ - "Negative", -}; - static const struct v4l2_pix_format vga_mode_t16[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -327,7 +320,6 @@ struct additional_sensor_data { const u8 data1[10]; const u8 data2[9]; const u8 data3[9]; - const u8 data4[4]; const u8 data5[6]; const u8 stream[4]; }; @@ -375,7 +367,7 @@ static const u8 n4_lt168g[] = { }; static const struct additional_sensor_data sensor_data[] = { - { /* 0: OM6802 */ +[SENSOR_OM6802] = { .n3 = {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}, .n4 = n4_om6802, @@ -392,14 +384,12 @@ static const struct additional_sensor_data sensor_data[] = { .data3 = {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, 0xff}, - .data4 = /*Freq (50/60Hz). Splitted for test purpose */ - {0x66, 0xca, 0xa8, 0xf0}, .data5 = /* this could be removed later */ {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23}, .stream = {0x0b, 0x04, 0x0a, 0x78}, }, - { /* 1: OTHER */ +[SENSOR_OTHER] = { .n3 = {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}, .n4 = n4_other, @@ -416,14 +406,12 @@ static const struct additional_sensor_data sensor_data[] = { .data3 = {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, 0xd9}, - .data4 = - {0x66, 0x00, 0xa8, 0xa8}, .data5 = {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69}, .stream = {0x0b, 0x04, 0x0a, 0x00}, }, - { /* 2: TAS5130A */ +[SENSOR_TAS5130A] = { .n3 = {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08}, .n4 = n4_tas5130a, @@ -440,14 +428,12 @@ static const struct additional_sensor_data sensor_data[] = { .data3 = {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0}, - .data4 = /* Freq (50/60Hz). Splitted for test purpose */ - {0x66, 0x00, 0xa8, 0xe8}, .data5 = {0x0c, 0x03, 0xab, 0x10, 0x81, 0x20}, .stream = {0x0b, 0x04, 0x0a, 0x40}, }, - { /* 3: LT168G */ +[SENSOR_LT168G] = { .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00}, .n4 = n4_lt168g, .n4sz = sizeof n4_lt168g, @@ -460,7 +446,6 @@ static const struct additional_sensor_data sensor_data[] = { 0xff}, .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, 0xff}, - .data4 = {0x66, 0x41, 0xa8, 0xf0}, .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b}, .stream = {0x0b, 0x04, 0x0a, 0x28}, }, @@ -469,6 +454,15 @@ static const struct additional_sensor_data sensor_data[] = { #define MAX_EFFECTS 7 /* easily done by soft, this table could be removed, * i keep it here just in case */ +static char *effects_control[MAX_EFFECTS] = { + "Normal", + "Emboss", /* disabled */ + "Monochrome", + "Sepia", + "Sketch", + "Sun Effect", /* disabled */ + "Negative", +}; static const u8 effects_table[MAX_EFFECTS][6] = { {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ @@ -480,40 +474,41 @@ static const u8 effects_table[MAX_EFFECTS][6] = { }; static const u8 gamma_table[GAMMA_MAX][17] = { - {0x00, 0x3e, 0x69, 0x85, 0x95, 0xa1, 0xae, 0xb9, /* 0 */ - 0xc2, 0xcb, 0xd4, 0xdb, 0xe3, 0xea, 0xf1, 0xf8, +/* gamma table from cam1690.ini */ + {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */ + 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb, 0xff}, - {0x00, 0x33, 0x5a, 0x75, 0x85, 0x93, 0xa1, 0xad, /* 1 */ - 0xb7, 0xc2, 0xcb, 0xd4, 0xde, 0xe7, 0xf0, 0xf7, + {0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d, /* 1 */ + 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1, 0xff}, - {0x00, 0x2f, 0x51, 0x6b, 0x7c, 0x8a, 0x99, 0xa6, /* 2 */ - 0xb1, 0xbc, 0xc6, 0xd0, 0xdb, 0xe4, 0xed, 0xf6, + {0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35, /* 2 */ + 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3, 0xff}, - {0x00, 0x29, 0x48, 0x60, 0x72, 0x81, 0x90, 0x9e, /* 3 */ - 0xaa, 0xb5, 0xbf, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, + {0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f, /* 3 */ + 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6, 0xff}, - {0x00, 0x23, 0x3f, 0x55, 0x68, 0x77, 0x86, 0x95, /* 4 */ - 0xa2, 0xad, 0xb9, 0xc6, 0xd2, 0xde, 0xe9, 0xf4, + {0x00, 0x04, 0x0B, 0x15, 0x20, 0x2d, 0x3b, 0x4a, /* 4 */ + 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9, 0xff}, - {0x00, 0x1b, 0x33, 0x48, 0x59, 0x69, 0x79, 0x87, /* 5 */ - 0x96, 0xa3, 0xb1, 0xbe, 0xcc, 0xda, 0xe7, 0xf3, + {0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58, /* 5 */ + 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec, 0xff}, - {0x00, 0x02, 0x10, 0x20, 0x32, 0x40, 0x57, 0x67, /* 6 */ + {0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67, /* 6 */ 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, - {0x00, 0x02, 0x14, 0x26, 0x38, 0x4a, 0x60, 0x70, /* 7 */ + {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, /* 7 */ 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff}, - {0x00, 0x10, 0x22, 0x35, 0x47, 0x5a, 0x69, 0x79, /* 8 */ - 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe0, 0xf0, + {0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79, /* 8 */ + 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0, 0xff}, - {0x00, 0x10, 0x26, 0x40, 0x54, 0x65, 0x75, 0x84, /* 9 */ - 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd6, 0xe0, 0xf0, + {0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84, /* 9 */ + 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2, 0xff}, - {0x00, 0x18, 0x2b, 0x44, 0x60, 0x70, 0x80, 0x8e, /* 10 */ - 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xd8, 0xe2, 0xf0, + {0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e, /* 10 */ + 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3, 0xff}, - {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8d, 0x9b, /* 11 */ + {0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b, /* 11 */ 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, 0xff}, {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ @@ -577,12 +572,11 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, } else { u8 *tmpbuf; - tmpbuf = kmalloc(len, GFP_KERNEL); + tmpbuf = kmemdup(buffer, len, GFP_KERNEL); if (!tmpbuf) { err("Out of memory"); return; } - memcpy(tmpbuf, buffer, len); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, @@ -625,7 +619,6 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev, kfree(tmpbuf); } -/* Reported as OM6802*/ static void om6802_sensor_init(struct gspca_dev *gspca_dev) { int i; @@ -703,12 +696,12 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autogain = AUTOGAIN_DEF; sd->mirror = MIRROR_DEF; sd->freq = FREQ_DEF; - sd->whitebalance = WHITE_BALANCE_DEF; + sd->awb = AWB_DEF; sd->sharpness = SHARPNESS_DEF; sd->effect = EFFECTS_DEF; - sd->red_balance = RED_BALANCE_DEF; - sd->blue_balance = BLUE_BALANCE_DEF; - sd->global_gain = global_gain_DEF; + sd->red_gain = RED_GAIN_DEF; + sd->blue_gain = BLUE_GAIN_DEF; + sd->green_gain = GAIN_DEF * 3 - RED_GAIN_DEF - BLUE_GAIN_DEF; return 0; } @@ -761,40 +754,59 @@ static void setgamma(struct gspca_dev *gspca_dev) reg_w_ixbuf(gspca_dev, 0x90, gamma_table[sd->gamma], sizeof gamma_table[0]); } -static void setglobalgain(struct gspca_dev *gspca_dev) -{ +static void setRGB(struct gspca_dev *gspca_dev) +{ struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, (sd->red_balance << 8) + 0x87); - reg_w(gspca_dev, (sd->blue_balance << 8) + 0x88); - reg_w(gspca_dev, (sd->global_gain << 8) + 0x89); + u8 all_gain_reg[6] = + {0x87, 0x00, 0x88, 0x00, 0x89, 0x00}; + + all_gain_reg[1] = sd->red_gain; + all_gain_reg[3] = sd->blue_gain; + all_gain_reg[5] = sd->green_gain; + reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); } -/* Generic fnc for r/b balance, exposure and whitebalance */ -static void setbalance(struct gspca_dev *gspca_dev) +/* Generic fnc for r/b balance, exposure and awb */ +static void setawb(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u16 reg80; - /* on whitebalance leave defaults values */ - if (sd->whitebalance) { - reg_w(gspca_dev, 0x3c80); - } else { - reg_w(gspca_dev, 0x3880); + reg80 = (sensor_data[sd->sensor].reg80 << 8) | 0x80; + + /* on awb leave defaults values */ + if (!sd->awb) { /* shoud we wait here.. */ - /* update and reset 'global gain' with webcam parameters */ - sd->red_balance = reg_r(gspca_dev, 0x0087); - sd->blue_balance = reg_r(gspca_dev, 0x0088); - sd->global_gain = reg_r(gspca_dev, 0x0089); - setglobalgain(gspca_dev); + /* update and reset RGB gains with webcam values */ + sd->red_gain = reg_r(gspca_dev, 0x0087); + sd->blue_gain = reg_r(gspca_dev, 0x0088); + sd->green_gain = reg_r(gspca_dev, 0x0089); + reg80 &= ~0x0400; /* AWB off */ } - + reg_w(gspca_dev, reg80); + reg_w(gspca_dev, reg80); } - - -static void setwhitebalance(struct gspca_dev *gspca_dev) +static void init_gains(struct gspca_dev *gspca_dev) { - setbalance(gspca_dev); + struct sd *sd = (struct sd *) gspca_dev; + u16 reg80; + u8 all_gain_reg[8] = + {0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00}; + + all_gain_reg[1] = sd->red_gain; + all_gain_reg[3] = sd->blue_gain; + all_gain_reg[5] = sd->green_gain; + reg80 = sensor_data[sd->sensor].reg80; + if (!sd->awb) + reg80 &= ~0x04; + all_gain_reg[7] = reg80; + reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); + + reg_w(gspca_dev, (sd->red_gain << 8) + 0x87); + reg_w(gspca_dev, (sd->blue_gain << 8) + 0x88); + reg_w(gspca_dev, (sd->green_gain << 8) + 0x89); } static void setsharpness(struct gspca_dev *gspca_dev) @@ -807,6 +819,38 @@ static void setsharpness(struct gspca_dev *gspca_dev) reg_w(gspca_dev, reg_to_write); } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 reg66; + u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 }; + + switch (sd->sensor) { + case SENSOR_LT168G: + if (sd->freq != 0) + freq[3] = 0xa8; + reg66 = 0x41; + break; + case SENSOR_OM6802: + reg66 = 0xca; + break; + default: + reg66 = 0x40; + break; + } + switch (sd->freq) { + case 0: /* no flicker */ + freq[3] = 0xf0; + break; + case 2: /* 60Hz */ + reg66 &= ~0x40; + break; + } + freq[1] = reg66; + + reg_w_buf(gspca_dev, freq, sizeof freq); +} + /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { @@ -901,13 +945,9 @@ static int sd_init(struct gspca_dev *gspca_dev) setgamma(gspca_dev); setcolors(gspca_dev); setsharpness(gspca_dev); - setwhitebalance(gspca_dev); + init_gains(gspca_dev); + setfreq(gspca_dev); - reg_w(gspca_dev, 0x2087); /* tied to white balance? */ - reg_w(gspca_dev, 0x2088); - reg_w(gspca_dev, 0x2089); - - reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); @@ -926,16 +966,16 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setflip(struct gspca_dev *gspca_dev) +static void setmirror(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 flipcmd[8] = + u8 hflipcmd[8] = {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}; if (sd->mirror) - flipcmd[3] = 0x01; + hflipcmd[3] = 0x01; - reg_w_buf(gspca_dev, flipcmd, sizeof flipcmd); + reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd); } static void seteffect(struct gspca_dev *gspca_dev) @@ -956,17 +996,6 @@ static void seteffect(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xfaa6); } -static void setlightfreq(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; - - if (sd->freq == 2) /* 60hz */ - freq[1] = 0x00; - - reg_w_buf(gspca_dev, freq, sizeof freq); -} - /* Is this really needed? * i added some module parameters for test with some users */ static void poll_sensor(struct gspca_dev *gspca_dev) @@ -979,9 +1008,7 @@ static void poll_sensor(struct gspca_dev *gspca_dev) static const u8 poll2[] = {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9, 0x73, 0x02, 0x73, 0x02, 0x60, 0x14}; - static const u8 poll3[] = - {0x87, 0x3f, 0x88, 0x20, 0x89, 0x2d}; - static const u8 poll4[] = + static const u8 noise03[] = /* (some differences / ms-drv) */ {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f, 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, 0xc2, 0x80, 0xc3, 0x10}; @@ -989,8 +1016,7 @@ static void poll_sensor(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "[Sensor requires polling]"); reg_w_buf(gspca_dev, poll1, sizeof poll1); reg_w_buf(gspca_dev, poll2, sizeof poll2); - reg_w_buf(gspca_dev, poll3, sizeof poll3); - reg_w_buf(gspca_dev, poll4, sizeof poll4); + reg_w_buf(gspca_dev, noise03, sizeof noise03); } static int sd_start(struct gspca_dev *gspca_dev) @@ -1025,12 +1051,7 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OM6802: om6802_sensor_init(gspca_dev); break; - case SENSOR_LT168G: - break; - case SENSOR_OTHER: - break; - default: -/* case SENSOR_TAS5130A: */ + case SENSOR_TAS5130A: i = 0; for (;;) { reg_w_buf(gspca_dev, tas5130a_sensor_init[i], @@ -1047,7 +1068,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } sensor = &sensor_data[sd->sensor]; - reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); + setfreq(gspca_dev); reg_r(gspca_dev, 0x0012); reg_w_buf(gspca_dev, t2, sizeof t2); reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); @@ -1080,7 +1101,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { - static u8 ffd9[] = { 0xff, 0xd9 }; + int pkt_type; if (data[0] == 0x5a) { /* Control Packet, after this came the header again, @@ -1090,84 +1111,88 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } data += 2; len -= 2; - if (data[0] == 0xff && data[1] == 0xd8) { - /* extra bytes....., could be processed too but would be - * a waste of time, right now leave the application and - * libjpeg do it for ourserlves.. */ - gspca_frame_add(gspca_dev, LAST_PACKET, - ffd9, 2); - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - } - - if (data[len - 2] == 0xff && data[len - 1] == 0xd9) { - /* Just in case, i have seen packets with the marker, - * other's do not include it... */ - len -= 2; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + if (data[0] == 0xff && data[1] == 0xd8) + pkt_type = FIRST_PACKET; + else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) + pkt_type = LAST_PACKET; + else + pkt_type = INTER_PACKET; + gspca_frame_add(gspca_dev, pkt_type, data, len); } - -static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->blue_balance = val; + sd->blue_gain = val; if (gspca_dev->streaming) reg_w(gspca_dev, (val << 8) + 0x88); return 0; } -static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->blue_balance; + *val = sd->blue_gain; return 0; } -static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->red_balance = val; + sd->red_gain = val; if (gspca_dev->streaming) reg_w(gspca_dev, (val << 8) + 0x87); return 0; } -static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->red_balance; + *val = sd->red_gain; return 0; } - - -static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + u16 psg, nsg; + + psg = sd->red_gain + sd->blue_gain + sd->green_gain; + nsg = val * 3; + sd->red_gain = sd->red_gain * nsg / psg; + if (sd->red_gain > 0x40) + sd->red_gain = 0x40; + else if (sd->red_gain < 0x10) + sd->red_gain = 0x10; + sd->blue_gain = sd->blue_gain * nsg / psg; + if (sd->blue_gain > 0x40) + sd->blue_gain = 0x40; + else if (sd->blue_gain < 0x10) + sd->blue_gain = 0x10; + sd->green_gain = sd->green_gain * nsg / psg; + if (sd->green_gain > 0x40) + sd->green_gain = 0x40; + else if (sd->green_gain < 0x10) + sd->green_gain = 0x10; - sd->global_gain = val; if (gspca_dev->streaming) - setglobalgain(gspca_dev); - + setRGB(gspca_dev); return 0; } -static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->global_gain; + *val = (sd->red_gain + sd->blue_gain + sd->green_gain) / 3; return 0; } - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1186,35 +1211,35 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) return *val; } -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->whitebalance = val; + sd->awb = val; if (gspca_dev->streaming) - setwhitebalance(gspca_dev); + setawb(gspca_dev); return 0; } -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->whitebalance; + *val = sd->awb; return *val; } -static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->mirror = val; if (gspca_dev->streaming) - setflip(gspca_dev); + setmirror(gspca_dev); return 0; } -static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1300,7 +1325,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) sd->freq = val; if (gspca_dev->streaming) - setlightfreq(gspca_dev); + setfreq(gspca_dev); return 0; } @@ -1368,7 +1393,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, case V4L2_CID_EFFECTS: if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) { strncpy((char *) menu->name, - effects_control[menu->index], 32); + effects_control[menu->index], + sizeof menu->name); return 0; } break; diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index c7b6eb1e04d..d9c5bf3449d 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -30,29 +30,46 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u16 brightness; + __u16 exposure; + __u16 gain; __u8 packet; }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { { { - .id = V4L2_CID_BRIGHTNESS, + .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", + .name = "Exposure", .minimum = 1, - .maximum = 0x15f, /* = 352 - 1 */ + .maximum = 0x18f, .step = 1, -#define BRIGHTNESS_DEF 0x14c - .default_value = BRIGHTNESS_DEF, +#define EXPOSURE_DEF 0x18f + .default_value = EXPOSURE_DEF, }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 0x7ff, + .step = 1, +#define GAIN_DEF 0x100 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, }, }; @@ -92,6 +109,14 @@ static const struct v4l2_pix_format sif_mode[] = { #define R14_AD_ROW_BEGINL 0x14 #define R15_AD_ROWBEGINH 0x15 #define R1C_AD_EXPOSE_TIMEL 0x1c +#define R20_GAIN_G1L 0x20 +#define R21_GAIN_G1H 0x21 +#define R22_GAIN_RL 0x22 +#define R23_GAIN_RH 0x23 +#define R24_GAIN_BL 0x24 +#define R25_GAIN_BH 0x25 +#define R26_GAIN_G2L 0x26 +#define R27_GAIN_G2H 0x27 #define R28_QUANT 0x28 #define R29_LINE 0x29 #define R2C_POLARITY 0x2c @@ -129,18 +154,6 @@ static const u8 eeprom_data[][3] = { {0x05, 0x09, 0xf1}, }; -static int reg_r(struct gspca_dev *gspca_dev, - __u16 index) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0x03, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, 1, - 500); - return gspca_dev->usb_buf[0]; -} /* write 1 byte */ static void reg_w1(struct gspca_dev *gspca_dev, @@ -183,7 +196,6 @@ static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) } reg_w1(gspca_dev, R07_TABLE_LEN, i); reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); - msleep(10); } /* this function is called at probe time */ @@ -197,53 +209,13 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); - sd->brightness = BRIGHTNESS_DEF; + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; return 0; } -static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev) -{ - int i; - static u8 reg_tb[] = { - R0C_AD_WIDTHL, - R0D_AD_WIDTHH, - R28_QUANT, - R29_LINE, - R2C_POLARITY, - R2D_POINT, - R2E_POINTH, - R2F_POINTB, - R30_POINTBH, - R2A_HIGH_BUDGET, - R2B_LOW_BUDGET, - R34_VID, - R35_VIDH, - R36_PID, - R37_PIDH, - R83_AD_IDH, - R10_AD_COL_BEGINL, - R11_AD_COL_BEGINH, - R14_AD_ROW_BEGINL, - R15_AD_ROWBEGINH, - 0 - }; - - i = 0; - do { - reg_r(gspca_dev, reg_tb[i]); - i++; - } while (reg_tb[i] != 0); -} - static void tv_8532_setReg(struct gspca_dev *gspca_dev) { - reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44); - /* begin active line */ - reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00); - /* mirror and digital gain */ - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); - /* = 0x84 */ - reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */ /******************************************************/ reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90); @@ -255,100 +227,43 @@ static void tv_8532_setReg(struct gspca_dev *gspca_dev) /* mirror and digital gain */ reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02); - - reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); /* = 0x84 */ } -static void tv_8532_PollReg(struct gspca_dev *gspca_dev) -{ - int i; - - /* strange polling from tgc */ - for (i = 0; i < 10; i++) { - reg_w1(gspca_dev, R2C_POLARITY, 0x10); - reg_w1(gspca_dev, R00_PART_CONTROL, - LATENT_CHANGE | EXPO_CHANGE); - reg_w1(gspca_dev, R31_UPD, 0x01); - } -} - /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { tv_8532WriteEEprom(gspca_dev); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V, - * slope rate 2 */ - reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00); - tv_8532ReadRegisters(gspca_dev); - reg_w1(gspca_dev, R3B_Test3, 0x0b); - reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190); - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f); - reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); - reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); - - /*******************************************************************/ - reg_w1(gspca_dev, R28_QUANT, 0x90); - /* no compress - fixed Q - quant 0 */ - reg_w1(gspca_dev, R29_LINE, 0x81); - /* 0x84; // CIF | 4 packet 0x29 */ - - /************************************************/ - reg_w1(gspca_dev, R2C_POLARITY, 0x10); - /* 0x48; //0x08; 0x2c */ - reg_w1(gspca_dev, R2D_POINT, 0x14); - /* 0x38; 0x2d */ - reg_w1(gspca_dev, R2E_POINTH, 0x01); - /* 0x04; 0x2e */ - reg_w1(gspca_dev, R2F_POINTB, 0x12); - /* 0x04; 0x2f */ - reg_w1(gspca_dev, R30_POINTBH, 0x01); - /* 0x04; 0x30 */ - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); - /* 0x00<-0x84 */ - /*************************************************/ - reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ - msleep(200); - reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ - /*************************************************/ - tv_8532_setReg(gspca_dev); - /*************************************************/ - reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ - /*************************************************/ - tv_8532_setReg(gspca_dev); - /*************************************************/ - tv_8532_PollReg(gspca_dev); return 0; } -static void setbrightness(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->brightness); + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->exposure); reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); /* 0x84 */ } -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V, - * slope rate 2 */ - reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00); - tv_8532ReadRegisters(gspca_dev); - reg_w1(gspca_dev, R3B_Test3, 0x0b); + reg_w2(gspca_dev, R20_GAIN_G1L, sd->gain); + reg_w2(gspca_dev, R22_GAIN_RL, sd->gain); + reg_w2(gspca_dev, R24_GAIN_BL, sd->gain); + reg_w2(gspca_dev, R26_GAIN_G2L, sd->gain); +} - reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190); - setbrightness(gspca_dev); +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */ reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); @@ -371,19 +286,15 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, R2E_POINTH, 0x01); reg_w1(gspca_dev, R2F_POINTB, 0x12); reg_w1(gspca_dev, R30_POINTBH, 0x01); - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + + tv_8532_setReg(gspca_dev); + + setexposure(gspca_dev); + setgain(gspca_dev); + /************************************************/ reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ msleep(200); - reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ - /************************************************/ - tv_8532_setReg(gspca_dev); - /************************************************/ - reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ - /************************************************/ - tv_8532_setReg(gspca_dev); - /************************************************/ - tv_8532_PollReg(gspca_dev); reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ gspca_dev->empty_packet = 0; /* check the empty packets */ @@ -428,21 +339,39 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data + gspca_dev->width + 5, gspca_dev->width); } -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->brightness = val; + sd->gain = val; if (gspca_dev->streaming) - setbrightness(gspca_dev); + setgain(gspca_dev); return 0; } -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->brightness; + *val = sd->gain; return 0; } diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 732c3dfe46f..b16fd47e8ce 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -39,6 +39,10 @@ struct sd { u8 vflip; u8 lightfreq; s8 sharpness; + u16 exposure; + u8 gain; + u8 autogain; + u8 backlight; u8 image_offset; @@ -77,6 +81,14 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_IDX 0 @@ -185,6 +197,66 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setsharpness, .get = sd_getsharpness, }, +#define GAIN_IDX 7 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 78, + .step = 1, +#define GAIN_DEF 0 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +#define EXPOSURE_IDX 8 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", +#define EXPOSURE_DEF 450 + .minimum = 0, + .maximum = 4095, + .step = 1, + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, +#define AUTOGAIN_IDX 9 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain and Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define BACKLIGHT_IDX 10 + { + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Backlight Compensation", + .minimum = 0, + .maximum = 15, + .step = 1, +#define BACKLIGHT_DEF 15 + .default_value = BACKLIGHT_DEF, + }, + .set = sd_setbacklight, + .get = sd_getbacklight, + }, }; /* table of the disabled controls */ @@ -192,33 +264,51 @@ static u32 ctrl_dis[] = { /* SENSOR_HV7131R 0 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) - | (1 << SHARPNESS_IDX), + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_MI0360 1 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) - | (1 << SHARPNESS_IDX), + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_MI1310_SOC 2 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) - | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_MI1320 3 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) - | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_MI1320_SOC 4 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) - | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_OV7660 5 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) - | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_OV7670 6 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) - | (1 << SHARPNESS_IDX), + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_PO1200 7 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) - | (1 << LIGHTFREQ_IDX), + | (1 << LIGHTFREQ_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_PO3130NC 8 */ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) - | (1 << SHARPNESS_IDX), + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), /* SENSOR_POxxxx 9 */ (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX), }; @@ -2748,11 +2838,11 @@ static const u8 poxxxx_init_common[][4] = { {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */ {0xb3, 0x23, 0x00, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */ {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x2c, 0x03, 0xcc}, {0xb3, 0x2d, 0x56, 0xcc}, @@ -2825,7 +2915,9 @@ static const u8 poxxxx_init_common[][4] = { {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x00, 0x40, 0xdd}, {0x00, 0x1d, 0x05, 0xaa}, - + {} +}; +static const u8 poxxxx_gamma[][4] = { {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */ {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x0a, 0xaa}, @@ -2867,19 +2959,9 @@ static const u8 poxxxx_init_common[][4] = { {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, - - {0x00, 0xaa, 0xff, 0xaa}, /* back light comp */ - {0x00, 0xc4, 0x03, 0xaa}, - {0x00, 0xc5, 0x19, 0xaa}, - {0x00, 0xc6, 0x03, 0xaa}, - {0x00, 0xc7, 0x91, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, - {0x00, 0xc9, 0xdd, 0xaa}, - {0x00, 0xca, 0x02, 0xaa}, - {0x00, 0xcb, 0x37, 0xaa}, - -/* read d1 */ - {0x00, 0xd1, 0x3c, 0xaa}, + {} +}; +static const u8 poxxxx_init_start_3[][4] = { {0x00, 0xb8, 0x28, 0xaa}, {0x00, 0xb9, 0x1e, 0xaa}, {0x00, 0xb6, 0x14, 0xaa}, @@ -2919,7 +3001,7 @@ static const u8 poxxxx_initVGA[][4] = { {0x00, 0x20, 0x11, 0xaa}, {0x00, 0x33, 0x38, 0xaa}, {0x00, 0xbb, 0x0d, 0xaa}, - {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, @@ -2935,7 +3017,7 @@ static const u8 poxxxx_initQVGA[][4] = { {0x00, 0x20, 0x33, 0xaa}, {0x00, 0x33, 0x38, 0xaa}, {0x00, 0xbb, 0x0d, 0xaa}, - {0xb3, 0x22, 0x00, 0xcc}, + {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */ {0xb3, 0x23, 0xf0, 0xcc}, {0xb3, 0x16, 0x01, 0xcc}, {0xb3, 0x17, 0x3f, 0xcc}, @@ -2959,9 +3041,6 @@ static const u8 poxxxx_init_end_1[][4] = { {0x00, 0xb3, 0x08, 0xaa}, {0x00, 0xb4, 0x0b, 0xaa}, {0x00, 0xb5, 0x0d, 0xaa}, - {0x00, 0x59, 0x7e, 0xaa}, /* sharpness */ - {0x00, 0x16, 0x00, 0xaa}, /* white balance */ - {0x00, 0x18, 0x00, 0xaa}, {} }; static const u8 poxxxx_init_end_2[][4] = { @@ -3068,37 +3147,84 @@ static const struct sensor_info vc0323_probe_data[] = { }; /* read 'len' bytes in gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, +static void reg_r_i(struct gspca_dev *gspca_dev, u16 req, u16 index, u16 len) { - usb_control_msg(gspca_dev->dev, + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1, /* value */ index, gspca_dev->usb_buf, len, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r err %d", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_r(struct gspca_dev *gspca_dev, + u16 req, + u16 index, + u16 len) +{ + reg_r_i(gspca_dev, req, index, len); +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + if (len == 1) + PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index, + gspca_dev->usb_buf[0]); + else + PDEBUG(D_USBI, "GET %02x 0001 %04x %02x %02x %02x", + req, index, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); +#endif } -static void reg_w(struct usb_device *dev, +static void reg_w_i(struct gspca_dev *gspca_dev, u16 req, u16 value, u16 index) { - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, NULL, 0, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_w(struct gspca_dev *gspca_dev, + u16 req, + u16 value, + u16 index) +{ +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); +#endif + reg_w_i(gspca_dev, req, value, index); } static u16 read_sensor_register(struct gspca_dev *gspca_dev, u16 address) { - struct usb_device *dev = gspca_dev->dev; u8 ldata, mdata, hdata; int retry = 50; @@ -3108,8 +3234,8 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, gspca_dev->usb_buf[0]); return 0; } - reg_w(dev, 0xa0, address, 0xb33a); - reg_w(dev, 0xa0, 0x02, 0xb339); + reg_w(gspca_dev, 0xa0, address, 0xb33a); + reg_w(gspca_dev, 0xa0, 0x02, 0xb339); do { reg_r(gspca_dev, 0xa1, 0xb33b, 1); @@ -3136,15 +3262,15 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int i, n; u16 value; const struct sensor_info *ptsensor_info; /*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ if (sd->flags & FL_SAMSUNG) { - reg_w(dev, 0xa0, 0x01, 0xb301); - reg_w(dev, 0x89, 0xf0ff, 0xffff); /* select the back sensor */ + reg_w(gspca_dev, 0xa0, 0x01, 0xb301); + reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); + /* select the back sensor */ } reg_r(gspca_dev, 0xa1, 0xbfcf, 1); @@ -3158,13 +3284,13 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) n = ARRAY_SIZE(vc0323_probe_data); } for (i = 0; i < n; i++) { - reg_w(dev, 0xa0, 0x02, 0xb334); - reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300); - reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300); - reg_w(dev, 0xa0, 0x01, 0xb308); - reg_w(dev, 0xa0, 0x0c, 0xb309); - reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); - reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); + reg_w(gspca_dev, 0xa0, 0x02, 0xb334); + reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300); + reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300); + reg_w(gspca_dev, 0xa0, 0x01, 0xb308); + reg_w(gspca_dev, 0xa0, 0x0c, 0xb309); + reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); + reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301); value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd); if (value == 0 && ptsensor_info->IdAdd == 0x82) value = read_sensor_register(gspca_dev, 0x83); @@ -3192,26 +3318,33 @@ static void i2c_write(struct gspca_dev *gspca_dev, u8 reg, const u8 *val, u8 size) /* 1 or 2 */ { - struct usb_device *dev = gspca_dev->dev; int retry; - reg_r(gspca_dev, 0xa1, 0xb33f, 1); +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + if (size == 1) + PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val); + else + PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]); +#endif + reg_r_i(gspca_dev, 0xa1, 0xb33f, 1); /*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ - reg_w(dev, 0xa0, size, 0xb334); - reg_w(dev, 0xa0, reg, 0xb33a); - reg_w(dev, 0xa0, val[0], 0xb336); + reg_w_i(gspca_dev, 0xa0, size, 0xb334); + reg_w_i(gspca_dev, 0xa0, reg, 0xb33a); + reg_w_i(gspca_dev, 0xa0, val[0], 0xb336); if (size > 1) - reg_w(dev, 0xa0, val[1], 0xb337); - reg_w(dev, 0xa0, 0x01, 0xb339); + reg_w_i(gspca_dev, 0xa0, val[1], 0xb337); + reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339); retry = 4; do { - reg_r(gspca_dev, 0xa1, 0xb33b, 1); + reg_r_i(gspca_dev, 0xa1, 0xb33b, 1); if (gspca_dev->usb_buf[0] == 0) break; msleep(20); } while (--retry > 0); if (retry <= 0) - PDEBUG(D_ERR, "i2c_write failed"); + PDEBUG(D_ERR, "i2c_write timeout"); } static void put_tab_to_reg(struct gspca_dev *gspca_dev, @@ -3221,13 +3354,12 @@ static void put_tab_to_reg(struct gspca_dev *gspca_dev, u16 ad = addr; for (j = 0; j < tabsize; j++) - reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); + reg_w(gspca_dev, 0xa0, tab[j], ad++); } static void usb_exchange(struct gspca_dev *gspca_dev, const u8 data[][4]) { - struct usb_device *dev = gspca_dev->dev; int i = 0; for (;;) { @@ -3235,7 +3367,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, default: return; case 0xcc: /* normal write */ - reg_w(dev, 0xa0, data[i][2], + reg_w(gspca_dev, 0xa0, data[i][2], (data[i][0]) << 8 | data[i][1]); break; case 0xaa: /* i2c op */ @@ -3259,7 +3391,33 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; + + sd->bridge = id->driver_info >> 8; + sd->flags = id->driver_info & 0xff; + + if (id->idVendor == 0x046d && + (id->idProduct == 0x0892 || id->idProduct == 0x0896)) + sd->sensor = SENSOR_POxxxx; /* no probe */ + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + sd->lightfreq = FREQ_DEF; + sd->sharpness = SHARPNESS_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->backlight = BACKLIGHT_DEF; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; int sensor; static u8 npkt[] = { /* number of packets per ISOC message */ @@ -3275,14 +3433,11 @@ static int sd_config(struct gspca_dev *gspca_dev, 128, /* POxxxx 9 */ }; - cam = &gspca_dev->cam; - sd->bridge = id->driver_info >> 8; - sd->flags = id->driver_info & 0xff; - if (id->idVendor == 0x046d && - (id->idProduct == 0x0892 || id->idProduct == 0x0896)) - sensor = SENSOR_POxxxx; - else + if (sd->sensor != SENSOR_POxxxx) sensor = vc032x_probe_sensor(gspca_dev); + else + sensor = sd->sensor; + switch (sensor) { case -1: PDEBUG(D_PROBE, "Unknown sensor..."); @@ -3321,6 +3476,7 @@ static int sd_config(struct gspca_dev *gspca_dev, } sd->sensor = sensor; + cam = &gspca_dev->cam; if (sd->bridge == BRIDGE_VC0321) { cam->cam_mode = vc0321_mode; cam->nmodes = ARRAY_SIZE(vc0321_mode); @@ -3349,15 +3505,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } } cam->npkt = npkt[sd->sensor]; - - sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; - sd->colors = COLOR_DEF; - sd->hflip = HFLIP_DEF; - sd->vflip = VFLIP_DEF; - sd->lightfreq = FREQ_DEF; - sd->sharpness = SHARPNESS_DEF; - gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; if (sd->sensor == SENSOR_OV7670) @@ -3365,28 +3512,19 @@ static int sd_config(struct gspca_dev *gspca_dev, if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); - reg_w(dev, 0x87, 0x00, 0x0f0f); - + reg_w(gspca_dev, 0x87, 0x00, 0x0f0f); reg_r(gspca_dev, 0x8b, 0, 3); - reg_w(dev, 0x88, 0x00, 0x0202); - } - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_POxxxx) { - reg_r(gspca_dev, 0xa1, 0xb300, 1); - if (gspca_dev->usb_buf[0] != 0) { - reg_w(gspca_dev->dev, 0xa0, 0x26, 0xb300); - reg_w(gspca_dev->dev, 0xa0, 0x04, 0xb300); - reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb300); + reg_w(gspca_dev, 0x88, 0x00, 0x0202); + if (sd->sensor == SENSOR_POxxxx) { + reg_r(gspca_dev, 0xa1, 0xb300, 1); + if (gspca_dev->usb_buf[0] != 0) { + reg_w(gspca_dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev, 0xa0, 0x04, 0xb300); + } + reg_w(gspca_dev, 0xa0, 0x00, 0xb300); } } - return 0; + return gspca_dev->usb_err; } static void setbrightness(struct gspca_dev *gspca_dev) @@ -3500,6 +3638,82 @@ static void setsharpness(struct gspca_dev *gspca_dev) break; } } +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) + return; + i2c_write(gspca_dev, 0x15, &sd->gain, 1); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data; + + if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) + return; + data = sd->exposure >> 8; + i2c_write(gspca_dev, 0x1a, &data, 1); + data = sd->exposure; + i2c_write(gspca_dev, 0x1b, &data, 1); +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 data[2] = {0x28, 0x3c}; + + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) + return; + i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1); +} + +static void setgamma(struct gspca_dev *gspca_dev) +{ +/*fixme:to do */ + usb_exchange(gspca_dev, poxxxx_gamma); +} + +static void setbacklight(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 v; + u8 data; + + data = (sd->backlight << 4) | 0x0f; + i2c_write(gspca_dev, 0xaa, &data, 1); + v = 613 + 12 * sd->backlight; + data = v >> 8; + i2c_write(gspca_dev, 0xc4, &data, 1); + data = v; + i2c_write(gspca_dev, 0xc5, &data, 1); + v = 1093 - 12 * sd->backlight; + data = v >> 8; + i2c_write(gspca_dev, 0xc6, &data, 1); + data = v; + i2c_write(gspca_dev, 0xc7, &data, 1); + v = 342 + 9 * sd->backlight; + data = v >> 8; + i2c_write(gspca_dev, 0xc8, &data, 1); + data = v; + i2c_write(gspca_dev, 0xc9, &data, 1); + v = 702 - 9 * sd->backlight; + data = v >> 8; + i2c_write(gspca_dev, 0xca, &data, 1); + data = v; + i2c_write(gspca_dev, 0xcb, &data, 1); +} + +static void setwb(struct gspca_dev *gspca_dev) +{ +/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/ + static const u8 data[2] = {0x00, 0x00}; + + i2c_write(gspca_dev, 0x16, &data[0], 1); + i2c_write(gspca_dev, 0x18, &data[1], 1); +} static int sd_start(struct gspca_dev *gspca_dev) { @@ -3516,17 +3730,17 @@ static int sd_start(struct gspca_dev *gspca_dev) /*fixme: back sensor only*/ if (sd->flags & FL_SAMSUNG) { - reg_w(gspca_dev->dev, 0x89, 0xf0ff, 0xffff); - reg_w(gspca_dev->dev, 0xa9, 0x8348, 0x000e); - reg_w(gspca_dev->dev, 0xa9, 0x0000, 0x001a); + reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); + reg_w(gspca_dev, 0xa9, 0x8348, 0x000e); + reg_w(gspca_dev, 0xa9, 0x0000, 0x001a); } /* Assume start use the good resolution from gspca_dev->mode */ if (sd->bridge == BRIDGE_VC0321) { - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfec); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfed); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfee); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfef); sd->image_offset = 46; } else { if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat @@ -3611,13 +3825,23 @@ static int sd_start(struct gspca_dev *gspca_dev) default: /* case SENSOR_POxxxx: */ usb_exchange(gspca_dev, poxxxx_init_common); + setgamma(gspca_dev); + setbacklight(gspca_dev); + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); + setsharpness(gspca_dev); + setautogain(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + usb_exchange(gspca_dev, poxxxx_init_start_3); if (mode) init = poxxxx_initQVGA; else init = poxxxx_initVGA; usb_exchange(gspca_dev, init); reg_r(gspca_dev, 0x8c, 0x0000, 3); - reg_w(gspca_dev->dev, 0xa0, + reg_w(gspca_dev, 0xa0, gspca_dev->usb_buf[2] & 1 ? 0 : 1, 0xb35c); msleep(300); @@ -3635,75 +3859,68 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_PO1200: case SENSOR_HV7131R: - reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + reg_w(gspca_dev, 0x89, 0x0400, 0x1415); break; case SENSOR_MI1310_SOC: - reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); + reg_w(gspca_dev, 0x89, 0x058c, 0x0000); break; } msleep(100); - setsharpness(gspca_dev); sethvflip(gspca_dev); setlightfreq(gspca_dev); } switch (sd->sensor) { case SENSOR_OV7670: - reg_w(gspca_dev->dev, 0x87, 0xffff, 0xffff); - reg_w(gspca_dev->dev, 0x88, 0xff00, 0xf0f1); - reg_w(gspca_dev->dev, 0xa0, 0x0000, 0xbfff); + reg_w(gspca_dev, 0x87, 0xffff, 0xffff); + reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1); + reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff); break; case SENSOR_POxxxx: - setcolors(gspca_dev); - setbrightness(gspca_dev); - setcontrast(gspca_dev); - - /* led on */ - msleep(80); - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); usb_exchange(gspca_dev, poxxxx_init_end_2); + setwb(gspca_dev); + msleep(80); /* led on */ + reg_w(gspca_dev, 0x89, 0xffff, 0xfdff); break; } - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { case SENSOR_MI1310_SOC: - reg_w(dev, 0x89, 0x058c, 0x00ff); + reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); break; case SENSOR_POxxxx: return; default: if (!(sd->flags & FL_SAMSUNG)) - reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(gspca_dev, 0x89, 0xffff, 0xffff); break; } - reg_w(dev, 0xa0, 0x01, 0xb301); - reg_w(dev, 0xa0, 0x09, 0xb003); + reg_w(gspca_dev, 0xa0, 0x01, 0xb301); + reg_w(gspca_dev, 0xa0, 0x09, 0xb003); } /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; struct sd *sd = (struct sd *) gspca_dev; if (!gspca_dev->present) return; /*fixme: is this useful?*/ if (sd->sensor == SENSOR_MI1310_SOC) - reg_w(dev, 0x89, 0x058c, 0x00ff); + reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); else if (!(sd->flags & FL_SAMSUNG)) - reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(gspca_dev, 0x89, 0xffff, 0xffff); if (sd->sensor == SENSOR_POxxxx) { - reg_w(dev, 0xa0, 0x26, 0xb300); - reg_w(dev, 0xa0, 0x04, 0xb300); - reg_w(dev, 0xa0, 0x00, 0xb300); + reg_w(gspca_dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev, 0xa0, 0x04, 0xb300); + reg_w(gspca_dev, 0xa0, 0x00, 0xb300); } } @@ -3726,17 +3943,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* The vc0321 sends some additional data after sending the complete * frame, we ignore this. */ if (sd->bridge == BRIDGE_VC0321) { - struct gspca_frame *frame; - int l; + int size, l; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - l = frame->data_end - frame->data; - if (len > frame->v4l2_buf.length - l) - len = frame->v4l2_buf.length - l; + l = gspca_dev->image_len; + size = gspca_dev->frsz; + if (len > size - l) + len = size - l; } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } @@ -3748,7 +3960,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) sd->brightness = val; if (gspca_dev->streaming) setbrightness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) @@ -3766,7 +3978,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = val; if (gspca_dev->streaming) setcontrast(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) @@ -3784,7 +3996,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) sd->colors = val; if (gspca_dev->streaming) setcolors(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) @@ -3802,7 +4014,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) sd->hflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -3820,7 +4032,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) sd->vflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -3838,7 +4050,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) sd->lightfreq = val; if (gspca_dev->streaming) setlightfreq(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) @@ -3856,7 +4068,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) sd->sharpness = val; if (gspca_dev->streaming) setsharpness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) @@ -3867,6 +4079,80 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (gspca_dev->streaming) + setautogain(gspca_dev); + + return gspca_dev->usb_err; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->backlight = val; + if (gspca_dev->streaming) + setbacklight(gspca_dev); + + return gspca_dev->usb_err; +} + +static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->backlight; + return 0; +} + static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c index 2fffe203bed..38a68591ce4 100644 --- a/drivers/media/video/gspca/w996Xcf.c +++ b/drivers/media/video/gspca/w996Xcf.c @@ -31,14 +31,10 @@ the sensor drivers to v4l2 sub drivers, and properly split of this driver from ov519.c */ -/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ -#define CONEX_CAM -#include "jpeg.h" - #define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ -#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET) -#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET) +#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET]) +#define UV_QUANTABLE (&sd->jpeg_hdr[JPEG_QT1_OFFSET]) static const struct v4l2_pix_format w9968cf_vga_mode[] = { {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, @@ -509,11 +505,6 @@ static int w9968cf_mode_init_regs(struct sd *sd) if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == V4L2_PIX_FMT_JPEG) { /* We may get called multiple times (usb isoc bw negotiat.) */ - if (!sd->jpeg_hdr) - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; - jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, sd->gspca_dev.width, 0x22); /* JPEG 420 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -562,9 +553,6 @@ static void w9968cf_stop0(struct sd *sd) reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ reg_w(sd, 0x16, 0x0000); /* stop video capture */ } - - kfree(sd->jpeg_hdr); - sd->jpeg_hdr = NULL; } /* The w9968cf docs say that a 0 sized packet means EOF (and also SOF diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index d02aa5c8472..0666038a51b 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -21,8 +21,9 @@ #define MODULE_NAME "zc3xx" +#ifdef CONFIG_INPUT #include <linux/input.h> -#include <linux/slab.h> +#endif #include "gspca.h" #include "jpeg.h" @@ -40,45 +41,53 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 brightness; u8 contrast; u8 gamma; u8 autogain; u8 lightfreq; u8 sharpness; u8 quality; /* image quality */ -#define QUALITY_MIN 40 -#define QUALITY_MAX 60 -#define QUALITY_DEF 50 +#define QUALITY_MIN 50 +#define QUALITY_MAX 80 +#define QUALITY_DEF 70 + u8 bridge; u8 sensor; /* Type of image sensor chip */ -/* !! values used in different tables */ -#define SENSOR_ADCM2700 0 -#define SENSOR_CS2102 1 -#define SENSOR_CS2102K 2 -#define SENSOR_GC0305 3 -#define SENSOR_HDCS2020b 4 -#define SENSOR_HV7131B 5 -#define SENSOR_HV7131C 6 -#define SENSOR_ICM105A 7 -#define SENSOR_MC501CB 8 -#define SENSOR_MI0360SOC 9 -#define SENSOR_OV7620 10 -/*#define SENSOR_OV7648 10 - same values */ -#define SENSOR_OV7630C 11 -#define SENSOR_PAS106 12 -#define SENSOR_PAS202B 13 -#define SENSOR_PB0330 14 /* (MI0360) */ -#define SENSOR_PO2030 15 -#define SENSOR_TAS5130CK 16 -#define SENSOR_TAS5130CXX 17 -#define SENSOR_TAS5130C_VF0250 18 -#define SENSOR_MAX 19 - unsigned short chip_revision; - - u8 *jpeg_hdr; + u16 chip_revision; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; +enum bridges { + BRIDGE_ZC301, + BRIDGE_ZC303, +}; +enum sensors { + SENSOR_ADCM2700, + SENSOR_CS2102, + SENSOR_CS2102K, + SENSOR_GC0305, + SENSOR_HDCS2020b, + SENSOR_HV7131B, + SENSOR_HV7131R, + SENSOR_ICM105A, + SENSOR_MC501CB, + SENSOR_MT9V111_1, /* (mi360soc) zc301 */ + SENSOR_MT9V111_3, /* (mi360soc) zc303 */ + SENSOR_OV7620, /* OV7648 - same values */ + SENSOR_OV7630C, + SENSOR_PAS106, + SENSOR_PAS202B, + SENSOR_PB0330, + SENSOR_PO2030, + SENSOR_TAS5130C, + SENSOR_TAS5130C_VF0250, + SENSOR_MAX }; /* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); @@ -93,6 +102,20 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { { { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", @@ -132,7 +155,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define LIGHTFREQ_IDX 3 +#define LIGHTFREQ_IDX 4 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -2058,6 +2081,7 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ {} }; +/* from lPEPI264v.inf (hv7131b!) */ static const struct usb_action hv7131r_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, @@ -2065,8 +2089,8 @@ static const struct usb_action hv7131r_InitialScale[] = { {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -2079,6 +2103,8 @@ static const struct usb_action hv7131r_InitialScale[] = { {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x000c}, {0xaa, 0x11, 0x0000}, @@ -2087,10 +2113,10 @@ static const struct usb_action hv7131r_InitialScale[] = { {0xaa, 0x15, 0x00e8}, {0xaa, 0x16, 0x0002}, {0xaa, 0x17, 0x0088}, - + {0xaa, 0x30, 0x000b}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x89, ZC3XX_R18D_YTARGET}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0xc0, 0x019b}, @@ -2100,96 +2126,44 @@ static const struct usb_action hv7131r_InitialScale[] = { {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf0, ZC3XX_R10B_RGB01}, - {0xa0, 0xf0, ZC3XX_R10C_RGB02}, - {0xa0, 0xf0, ZC3XX_R10D_RGB10}, - {0xa0, 0x60, ZC3XX_R10E_RGB11}, - {0xa0, 0xf0, ZC3XX_R10F_RGB12}, - {0xa0, 0xf0, ZC3XX_R110_RGB20}, - {0xa0, 0xf0, ZC3XX_R111_RGB21}, - {0xa0, 0x60, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x25, 0x0007}, - {0xaa, 0x26, 0x0053}, - {0xaa, 0x27, 0x0000}, - - {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */ - {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */ - {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa1, 0x01, 0x001d}, - {0xa1, 0x01, 0x001e}, - {0xa1, 0x01, 0x001f}, - {0xa1, 0x01, 0x0020}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; - static const struct usb_action hv7131r_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 1e0 */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x000c}, {0xaa, 0x11, 0x0000}, {0xaa, 0x13, 0x0000}, {0xaa, 0x14, 0x0001}, - {0xaa, 0x15, 0x00e8}, + {0xaa, 0x15, 0x00e6}, {0xaa, 0x16, 0x0002}, - {0xaa, 0x17, 0x0088}, - - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00 */ - + {0xaa, 0x17, 0x0086}, + {0xaa, 0x30, 0x000b}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x89, ZC3XX_R18D_YTARGET}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0xc0, 0x019b}, @@ -2199,58 +2173,114 @@ static const struct usb_action hv7131r_Initial[] = { {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - /* read the i2c chips ident */ - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf0, ZC3XX_R10B_RGB01}, - {0xa0, 0xf0, ZC3XX_R10C_RGB02}, - {0xa0, 0xf0, ZC3XX_R10D_RGB10}, - {0xa0, 0x60, ZC3XX_R10E_RGB11}, - {0xa0, 0xf0, ZC3XX_R10F_RGB12}, - {0xa0, 0xf0, ZC3XX_R110_RGB20}, - {0xa0, 0xf0, ZC3XX_R111_RGB21}, - {0xa0, 0x60, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action hv7131r_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x25, 0x0007}, - {0xaa, 0x26, 0x0053}, - {0xaa, 0x27, 0x0000}, - - {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */ - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */ - {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */ - + {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, - - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa1, 0x01, 0x001d}, - {0xa1, 0x01, 0x001e}, - {0xa1, 0x01, 0x001f}, - {0xa1, 0x01, 0x0020}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x18, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action hv7131r_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x08, ZC3XX_R020_HSYNC_3}, {} }; @@ -3334,7 +3364,7 @@ static const struct usb_action ov7620_NoFliker[] = { {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */ /* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc - - if mode1 (320x240) */ + * if mode1 (320x240) */ /* ?? was {0xa0, 0x00, 0x0039}, * 00,00,00,dd * {0xa1, 0x01, 0x0037}, */ @@ -3423,7 +3453,6 @@ static const struct usb_action ov7630c_InitialScale[] = { {0xa0, 0xf8, ZC3XX_R110_RGB20}, {0xa0, 0xf8, ZC3XX_R111_RGB21}, {0xa0, 0x50, ZC3XX_R112_RGB22}, -/* 0x03, */ {0xa1, 0x01, 0x0008}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ @@ -3703,7 +3732,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */ {0xaa, 0x0e, 0x0002}, {0xaa, 0x14, 0x0081}, -/* Other registors */ +/* Other registers */ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* Frame retreiving */ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, @@ -3714,7 +3743,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */ /* Sharpness */ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, -/* Other registors */ +/* Other registers */ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* Auto exposure and white balance */ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, @@ -3821,7 +3850,7 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */ {0xaa, 0x0e, 0x0002}, {0xaa, 0x14, 0x0081}, -/* Other registors */ +/* Other registers */ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* Frame retreiving */ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, @@ -3832,7 +3861,7 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */ /* Sharpness */ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, -/* Other registors */ +/* Other registers */ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* Auto exposure and white balance */ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, @@ -4225,8 +4254,8 @@ static const struct usb_action pas202b_NoFlikerScale[] = { {} }; -/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */ -static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ +/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */ +static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, @@ -4237,14 +4266,14 @@ static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ -/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, @@ -4254,18 +4283,18 @@ static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ - {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/ + {0xaa, 0x20, 0x5100}, + {0xaa, 0x35, 0x507f}, {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x0028}, + {0xaa, 0x2b, 0x007f}, {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/ {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/ {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, @@ -4275,12 +4304,12 @@ static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */ + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ +static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, @@ -4291,14 +4320,14 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ -/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, @@ -4308,7 +4337,7 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xaa, 0x03, 0x01e7}, {0xaa, 0x04, 0x0287}, {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x20, 0x5100}, {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, @@ -4319,7 +4348,7 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/ {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, @@ -4329,12 +4358,12 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/ + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action mi360soc_AE50HZ[] = { +static const struct usb_action mt9v111_1_AE50HZ[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0562}, @@ -4357,7 +4386,7 @@ static const struct usb_action mi360soc_AE50HZ[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mi360soc_AE50HZScale[] = { +static const struct usb_action mt9v111_1_AE50HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0509}, @@ -4379,11 +4408,11 @@ static const struct usb_action mi360soc_AE50HZScale[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mi360soc_AE60HZ[] = { +static const struct usb_action mt9v111_1_AE60HZ[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xbb, 0x00, 0x053d}, - {0xbb, 0x01, 0x096e}, + {0xaa, 0x05, 0x003d}, + {0xaa, 0x09, 0x016e}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, @@ -4402,7 +4431,7 @@ static const struct usb_action mi360soc_AE60HZ[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mi360soc_AE60HZScale[] = { +static const struct usb_action mt9v111_1_AE60HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0509}, @@ -4424,7 +4453,7 @@ static const struct usb_action mi360soc_AE60HZScale[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mi360soc_AENoFliker[] = { +static const struct usb_action mt9v111_1_AENoFliker[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0509}, @@ -4447,7 +4476,7 @@ static const struct usb_action mi360soc_AENoFliker[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action mi360soc_AENoFlikerScale[] = { +static const struct usb_action mt9v111_1_AENoFlikerScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xbb, 0x00, 0x0534}, @@ -4470,6 +4499,251 @@ static const struct usb_action mi360soc_AENoFlikerScale[] = { {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; +/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */ +static const struct usb_action mt9v111_3_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */ + {0xaa, 0x06, 0x0000}, /* operating mode control */ + {0xaa, 0x08, 0x0483}, /* output format control */ + /* H red first, V red or blue first, + * raw Bayer, auto flicker */ + {0xaa, 0x01, 0x0004}, /* select sensor core registers */ + {0xaa, 0x08, 0x0006}, /* row start */ + {0xaa, 0x02, 0x0011}, /* column start */ + {0xaa, 0x03, 0x01e5}, /* window height - 1 */ + {0xaa, 0x04, 0x0285}, /* window width - 1 */ + {0xaa, 0x07, 0x3002}, /* output control */ + {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */ + {0xaa, 0x35, 0x007f}, /* global gain */ + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x007f}, /* green1 gain */ + {0xaa, 0x2c, 0x007f}, /* blue gain */ + {0xaa, 0x2d, 0x007f}, /* red gain */ + {0xaa, 0x2e, 0x007f}, /* green2 gain */ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action mt9v111_3_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0001}, + {0xaa, 0x06, 0x0000}, + {0xaa, 0x08, 0x0483}, + {0xaa, 0x01, 0x0004}, + {0xaa, 0x08, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x35, 0x007f}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x007f}, + {0xaa, 0x2c, 0x007f}, + {0xaa, 0x2d, 0x007f}, + {0xaa, 0x2e, 0x007f}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action mt9v111_3_AE50HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, /* horizontal blanking */ + {0xaa, 0x09, 0x01ce}, /* shutter width */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AE50HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x01ce}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AE60HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x0083}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AE60HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x0083}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AENoFliker[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0034}, + {0xaa, 0x09, 0x0260}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mt9v111_3_AENoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0034}, + {0xaa, 0x09, 0x0260}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; static const struct usb_action pb0330_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, @@ -4912,419 +5186,7 @@ static const struct usb_action po2030_NoFliker[] = { {} }; -/* TEST */ -static const struct usb_action tas5130cK_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x01, 0x003b}, - {0xa0, 0x0e, 0x003a}, - {0xa0, 0x01, 0x0038}, - {0xa0, 0x0b, 0x0039}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x0b, 0x0039}, - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xE7, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x87, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2c, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, - {0xa0, 0x65, ZC3XX_R118_BGAIN}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf1, ZC3XX_R10B_RGB01}, - {0xa0, 0x03, ZC3XX_R10C_RGB02}, - {0xa0, 0xfe, ZC3XX_R10D_RGB10}, - {0xa0, 0x51, ZC3XX_R10E_RGB11}, - {0xa0, 0xf1, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0x03, ZC3XX_R111_RGB21}, - {0xa0, 0x51, ZC3XX_R112_RGB22}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */ - {0xa0, 0x51, ZC3XX_R121_GAMMA01}, - {0xa0, 0x6e, ZC3XX_R122_GAMMA02}, - {0xa0, 0x8c, ZC3XX_R123_GAMMA03}, - {0xa0, 0xa2, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb6, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc8, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd6, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe2, ZC3XX_R128_GAMMA08}, - {0xa0, 0xed, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x12, ZC3XX_R130_GAMMA10}, - {0xa0, 0x1b, ZC3XX_R131_GAMMA11}, - {0xa0, 0x1d, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, - {0xa0, 0x15, ZC3XX_R134_GAMMA14}, - {0xa0, 0x12, ZC3XX_R135_GAMMA15}, - {0xa0, 0x0f, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x05, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf1, ZC3XX_R10B_RGB01}, - {0xa0, 0x03, ZC3XX_R10C_RGB02}, - {0xa0, 0xfe, ZC3XX_R10D_RGB10}, - {0xa0, 0x51, ZC3XX_R10E_RGB11}, - {0xa0, 0xf1, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0x03, ZC3XX_R111_RGB21}, - {0xa0, 0x51, ZC3XX_R112_RGB22}, - {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x09, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x34, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {} -}; - -static const struct usb_action tas5130cK_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x01, 0x003b}, - {0xa0, 0x0e, 0x003a}, - {0xa0, 0x01, 0x0038}, - {0xa0, 0x0b, 0x0039}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x0b, 0x0039}, - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xe5, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x85, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x50, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2C, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa0, 0x61, ZC3XX_R116_RGAIN}, - {0xa0, 0x65, ZC3XX_R118_BGAIN}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf1, ZC3XX_R10B_RGB01}, - {0xa0, 0x03, ZC3XX_R10C_RGB02}, - {0xa0, 0xfe, ZC3XX_R10D_RGB10}, - {0xa0, 0x51, ZC3XX_R10E_RGB11}, - {0xa0, 0xf1, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0x03, ZC3XX_R111_RGB21}, - {0xa0, 0x51, ZC3XX_R112_RGB22}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */ - {0xa0, 0x51, ZC3XX_R121_GAMMA01}, - {0xa0, 0x6e, ZC3XX_R122_GAMMA02}, - {0xa0, 0x8c, ZC3XX_R123_GAMMA03}, - {0xa0, 0xa2, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb6, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc8, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd6, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe2, ZC3XX_R128_GAMMA08}, - {0xa0, 0xed, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x12, ZC3XX_R130_GAMMA10}, - {0xa0, 0x1b, ZC3XX_R131_GAMMA11}, - {0xa0, 0x1d, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, - {0xa0, 0x15, ZC3XX_R134_GAMMA14}, - {0xa0, 0x12, ZC3XX_R135_GAMMA15}, - {0xa0, 0x0f, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x05, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf1, ZC3XX_R10B_RGB01}, - {0xa0, 0x03, ZC3XX_R10C_RGB02}, - {0xa0, 0xfe, ZC3XX_R10D_RGB10}, - {0xa0, 0x51, ZC3XX_R10E_RGB11}, - {0xa0, 0xf1, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0x03, ZC3XX_R111_RGB21}, - {0xa0, 0x51, ZC3XX_R112_RGB22}, - {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x62, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xaa, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x30, 0x0007}, - {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x00, 0x0007}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {} -}; - -static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */ +static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -5361,7 +5223,7 @@ static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, {} }; -static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */ +static const struct usb_action tas5130c_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, @@ -5397,7 +5259,7 @@ static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, {} }; -static const struct usb_action tas5130cxx_50HZ[] = { +static const struct usb_action tas5130c_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */ @@ -5422,7 +5284,7 @@ static const struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; -static const struct usb_action tas5130cxx_50HZScale[] = { +static const struct usb_action tas5130c_50HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ @@ -5447,7 +5309,7 @@ static const struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; -static const struct usb_action tas5130cxx_60HZ[] = { +static const struct usb_action tas5130c_60HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */ @@ -5472,7 +5334,7 @@ static const struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; -static const struct usb_action tas5130cxx_60HZScale[] = { +static const struct usb_action tas5130c_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ @@ -5497,7 +5359,7 @@ static const struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; -static const struct usb_action tas5130cxx_NoFliker[] = { +static const struct usb_action tas5130c_NoFliker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */ @@ -5523,7 +5385,7 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {} }; -static const struct usb_action tas5130cxx_NoFlikerScale[] = { +static const struct usb_action tas5130c_NoFlikerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */ @@ -5824,13 +5686,22 @@ static const struct usb_action tas5130c_vf0250_NoFliker[] = { static u8 reg_r_i(struct gspca_dev *gspca_dev, u16 index) { - usb_control_msg(gspca_dev->dev, + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 0xa1, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x01, /* value */ index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r_i err %d", ret); + gspca_dev->usb_err = ret; + return 0; + } return gspca_dev->usb_buf[0]; } @@ -5844,24 +5715,32 @@ static u8 reg_r(struct gspca_dev *gspca_dev, return ret; } -static void reg_w_i(struct usb_device *dev, +static void reg_w_i(struct gspca_dev *gspca_dev, u8 value, u16 index) { - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), 0xa0, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, NULL, 0, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_i err %d", ret); + gspca_dev->usb_err = ret; + } } -static void reg_w(struct usb_device *dev, +static void reg_w(struct gspca_dev *gspca_dev, u8 value, u16 index) { PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value); - reg_w_i(dev, value, index); + reg_w_i(gspca_dev, value, index); } static u16 i2c_read(struct gspca_dev *gspca_dev, @@ -5870,8 +5749,10 @@ static u16 i2c_read(struct gspca_dev *gspca_dev, u8 retbyte; u16 retval; - reg_w_i(gspca_dev->dev, reg, 0x0092); - reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */ + if (gspca_dev->usb_err < 0) + return 0; + reg_w_i(gspca_dev, reg, 0x0092); + reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */ msleep(20); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) @@ -5890,10 +5771,12 @@ static u8 i2c_write(struct gspca_dev *gspca_dev, { u8 retbyte; - reg_w_i(gspca_dev->dev, reg, 0x92); - reg_w_i(gspca_dev->dev, valL, 0x93); - reg_w_i(gspca_dev->dev, valH, 0x94); - reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ + if (gspca_dev->usb_err < 0) + return 0; + reg_w_i(gspca_dev, reg, 0x92); + reg_w_i(gspca_dev, valL, 0x93); + reg_w_i(gspca_dev, valH, 0x94); + reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */ msleep(1); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) @@ -5909,7 +5792,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, while (action->req) { switch (action->req) { case 0xa0: /* write register */ - reg_w(gspca_dev->dev, action->val, action->idx); + reg_w(gspca_dev, action->val, action->idx); break; case 0xa1: /* read status */ reg_r(gspca_dev, action->idx); @@ -5958,38 +5841,37 @@ static void setmatrix(struct gspca_dev *gspca_dev) static const u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; static const u8 *matrix_tb[SENSOR_MAX] = { - adcm2700_matrix, /* SENSOR_ADCM2700 0 */ - ov7620_matrix, /* SENSOR_CS2102 1 */ - NULL, /* SENSOR_CS2102K 2 */ - gc0305_matrix, /* SENSOR_GC0305 3 */ - NULL, /* SENSOR_HDCS2020b 4 */ - NULL, /* SENSOR_HV7131B 5 */ - NULL, /* SENSOR_HV7131C 6 */ - NULL, /* SENSOR_ICM105A 7 */ - NULL, /* SENSOR_MC501CB 8 */ - gc0305_matrix, /* SENSOR_MI0360SOC 9 */ - ov7620_matrix, /* SENSOR_OV7620 10 */ - NULL, /* SENSOR_OV7630C 11 */ - NULL, /* SENSOR_PAS106 12 */ - pas202b_matrix, /* SENSOR_PAS202B 13 */ - gc0305_matrix, /* SENSOR_PB0330 14 */ - po2030_matrix, /* SENSOR_PO2030 15 */ - NULL, /* SENSOR_TAS5130CK 16 */ - tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */ - vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */ + [SENSOR_ADCM2700] = adcm2700_matrix, + [SENSOR_CS2102] = ov7620_matrix, + [SENSOR_CS2102K] = NULL, + [SENSOR_GC0305] = gc0305_matrix, + [SENSOR_HDCS2020b] = NULL, + [SENSOR_HV7131B] = NULL, + [SENSOR_HV7131R] = NULL, + [SENSOR_ICM105A] = po2030_matrix, + [SENSOR_MC501CB] = NULL, + [SENSOR_MT9V111_1] = gc0305_matrix, + [SENSOR_MT9V111_3] = gc0305_matrix, + [SENSOR_OV7620] = ov7620_matrix, + [SENSOR_OV7630C] = NULL, + [SENSOR_PAS106] = NULL, + [SENSOR_PAS202B] = pas202b_matrix, + [SENSOR_PB0330] = gc0305_matrix, + [SENSOR_PO2030] = po2030_matrix, + [SENSOR_TAS5130C] = tas5130c_matrix, + [SENSOR_TAS5130C_VF0250] = vf0250_matrix, }; matrix = matrix_tb[sd->sensor]; if (matrix == NULL) return; /* matrix already loaded */ for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++) - reg_w(gspca_dev->dev, matrix[i], 0x010a + i); + reg_w(gspca_dev, matrix[i], 0x010a + i); } static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int sharpness; static const u8 sharpness_tb[][2] = { {0x02, 0x03}, @@ -5999,21 +5881,23 @@ static void setsharpness(struct gspca_dev *gspca_dev) }; sharpness = sd->sharpness; - reg_w(dev, sharpness_tb[sharpness][0], 0x01c6); + reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6); reg_r(gspca_dev, 0x01c8); reg_r(gspca_dev, 0x01c9); reg_r(gspca_dev, 0x01ca); - reg_w(dev, sharpness_tb[sharpness][1], 0x01cb); + reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb); } static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; const u8 *Tgamma; - int g, i, k, adj, gp; + int g, i, brightness, contrast, adj, gp1, gp2; u8 gr[16]; - static const u8 delta_tb[16] = /* delta for contrast */ + static const u8 delta_b[16] = /* delta for brightness */ + {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, + 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18}; + static const u8 delta_c[16] = /* delta for contrast */ {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02}; static const u8 gamma_tb[6][16] = { @@ -6033,38 +5917,37 @@ static void setcontrast(struct gspca_dev *gspca_dev) Tgamma = gamma_tb[sd->gamma - 1]; - k = ((int) sd->contrast - 128); /* -128 / 128 */ + contrast = ((int) sd->contrast - 128); /* -128 / 127 */ + brightness = ((int) sd->brightness - 128); /* -128 / 92 */ adj = 0; - gp = 0; + gp1 = gp2 = 0; for (i = 0; i < 16; i++) { - g = Tgamma[i] - delta_tb[i] * k / 256 - adj / 2; + g = Tgamma[i] + delta_b[i] * brightness / 256 + - delta_c[i] * contrast / 256 - adj / 2; if (g > 0xff) g = 0xff; else if (g < 0) g = 0; - reg_w(dev, g, 0x0120 + i); /* gamma */ - if (k > 0) + reg_w(gspca_dev, g, 0x0120 + i); /* gamma */ + if (contrast > 0) adj--; - else + else if (contrast < 0) adj++; - - if (i != 0) { - if (gp == 0) - gr[i - 1] = 0; - else - gr[i - 1] = g - gp; - } - gp = g; + if (i > 1) + gr[i - 1] = (g - gp2) / 2; + else if (i != 0) + gr[0] = gp1 == 0 ? 0 : (g - gp1); + gp2 = gp1; + gp1 = g; } - gr[15] = gr[14] / 2; + gr[15] = (0xff - gp2) / 2; for (i = 0; i < 16; i++) - reg_w(dev, gr[i], 0x0130 + i); /* gradient */ + reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ } static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; u8 frxt; switch (sd->sensor) { @@ -6077,9 +5960,9 @@ static void setquality(struct gspca_dev *gspca_dev) return; } /*fixme: is it really 0008 0007 0018 for all other sensors? */ - reg_w(dev, QUANT_VAL, 0x0008); + reg_w(gspca_dev, QUANT_VAL, 0x0008); frxt = 0x30; - reg_w(dev, frxt, 0x0007); + reg_w(gspca_dev, frxt, 0x0007); #if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2 frxt = 0xff; #elif QUANT_VAL == 3 @@ -6089,7 +5972,7 @@ static void setquality(struct gspca_dev *gspca_dev) #else frxt = 0x20; #endif - reg_w(dev, frxt, 0x0018); + reg_w(gspca_dev, frxt, 0x0018); } /* Matches the sensor's internal frame rate to the lighting frequency. @@ -6097,87 +5980,86 @@ static void setquality(struct gspca_dev *gspca_dev) * 50Hz, for European and Asian lighting (default) * 60Hz, for American lighting * 0 = No Fliker (for outdoore usage) - * Returns: 0 for success */ -static int setlightfreq(struct gspca_dev *gspca_dev) +static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, mode; const struct usb_action *zc3_freq; static const struct usb_action *freq_tb[SENSOR_MAX][6] = { -/* SENSOR_ADCM2700 0 */ + [SENSOR_ADCM2700] = {adcm2700_NoFliker, adcm2700_NoFliker, adcm2700_50HZ, adcm2700_50HZ, adcm2700_60HZ, adcm2700_60HZ}, -/* SENSOR_CS2102 1 */ + [SENSOR_CS2102] = {cs2102_NoFliker, cs2102_NoFlikerScale, cs2102_50HZ, cs2102_50HZScale, cs2102_60HZ, cs2102_60HZScale}, -/* SENSOR_CS2102K 2 */ + [SENSOR_CS2102K] = {cs2102_NoFliker, cs2102_NoFlikerScale, NULL, NULL, /* currently disabled */ NULL, NULL}, -/* SENSOR_GC0305 3 */ + [SENSOR_GC0305] = {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, gc0305_60HZ, gc0305_60HZ}, -/* SENSOR_HDCS2020b 4 */ + [SENSOR_HDCS2020b] = {hdcs2020b_NoFliker, hdcs2020b_NoFliker, hdcs2020b_50HZ, hdcs2020b_50HZ, hdcs2020b_60HZ, hdcs2020b_60HZ}, -/* SENSOR_HV7131B 5 */ + [SENSOR_HV7131B] = {hv7131b_NoFliker, hv7131b_NoFlikerScale, hv7131b_50HZ, hv7131b_50HZScale, hv7131b_60HZ, hv7131b_60HZScale}, -/* SENSOR_HV7131C 6 */ - {NULL, NULL, - NULL, NULL, - NULL, NULL}, -/* SENSOR_ICM105A 7 */ + [SENSOR_HV7131R] = + {hv7131r_NoFliker, hv7131r_NoFlikerScale, + hv7131r_50HZ, hv7131r_50HZScale, + hv7131r_60HZ, hv7131r_60HZScale}, + [SENSOR_ICM105A] = {icm105a_NoFliker, icm105a_NoFlikerScale, icm105a_50HZ, icm105a_50HZScale, icm105a_60HZ, icm105a_60HZScale}, -/* SENSOR_MC501CB 8 */ + [SENSOR_MC501CB] = {mc501cb_NoFliker, mc501cb_NoFlikerScale, mc501cb_50HZ, mc501cb_50HZScale, mc501cb_60HZ, mc501cb_60HZScale}, -/* SENSOR_MI0360SOC 9 */ - {mi360soc_AENoFliker, mi360soc_AENoFlikerScale, - mi360soc_AE50HZ, mi360soc_AE50HZScale, - mi360soc_AE60HZ, mi360soc_AE60HZScale}, -/* SENSOR_OV7620 10 */ + [SENSOR_MT9V111_1] = + {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale, + mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale, + mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale}, + [SENSOR_MT9V111_3] = + {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale, + mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale, + mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale}, + [SENSOR_OV7620] = {ov7620_NoFliker, ov7620_NoFliker, ov7620_50HZ, ov7620_50HZ, ov7620_60HZ, ov7620_60HZ}, -/* SENSOR_OV7630C 11 */ + [SENSOR_OV7630C] = {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 12 */ + [SENSOR_PAS106] = {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, -/* SENSOR_PAS202B 13 */ + [SENSOR_PAS202B] = {pas202b_NoFliker, pas202b_NoFlikerScale, pas202b_50HZ, pas202b_50HZScale, pas202b_60HZ, pas202b_60HZScale}, -/* SENSOR_PB0330 14 */ + [SENSOR_PB0330] = {pb0330_NoFliker, pb0330_NoFlikerScale, pb0330_50HZ, pb0330_50HZScale, pb0330_60HZ, pb0330_60HZScale}, -/* SENSOR_PO2030 15 */ + [SENSOR_PO2030] = {po2030_NoFliker, po2030_NoFliker, po2030_50HZ, po2030_50HZ, po2030_60HZ, po2030_60HZ}, -/* SENSOR_TAS5130CK 16 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130CXX 17 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130C_VF0250 18 */ + [SENSOR_TAS5130C] = + {tas5130c_NoFliker, tas5130c_NoFlikerScale, + tas5130c_50HZ, tas5130c_50HZScale, + tas5130c_60HZ, tas5130c_60HZScale}, + [SENSOR_TAS5130C_VF0250] = {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, @@ -6188,29 +6070,28 @@ static int setlightfreq(struct gspca_dev *gspca_dev) if (mode) i++; /* 320x240 */ zc3_freq = freq_tb[sd->sensor][i]; - if (zc3_freq != NULL) { - usb_exchange(gspca_dev, zc3_freq); - switch (sd->sensor) { - case SENSOR_GC0305: - if (mode /* if 320x240 */ - && sd->lightfreq == 1) /* and 50Hz */ - reg_w(gspca_dev->dev, 0x85, 0x018d); - /* win: 0x80, 0x018d */ - break; - case SENSOR_OV7620: - if (!mode) { /* if 640x480 */ - if (sd->lightfreq != 0) /* and 50 or 60 Hz */ - reg_w(gspca_dev->dev, 0x40, 0x0002); - else - reg_w(gspca_dev->dev, 0x44, 0x0002); - } - break; - case SENSOR_PAS202B: - reg_w(gspca_dev->dev, 0x00, 0x01a7); - break; + if (zc3_freq == NULL) + return; + usb_exchange(gspca_dev, zc3_freq); + switch (sd->sensor) { + case SENSOR_GC0305: + if (mode /* if 320x240 */ + && sd->lightfreq == 1) /* and 50Hz */ + reg_w(gspca_dev, 0x85, 0x018d); + /* win: 0x80, 0x018d */ + break; + case SENSOR_OV7620: + if (!mode) { /* if 640x480 */ + if (sd->lightfreq != 0) /* and 50 or 60 Hz */ + reg_w(gspca_dev, 0x40, 0x0002); + else + reg_w(gspca_dev, 0x44, 0x0002); } + break; + case SENSOR_PAS202B: + reg_w(gspca_dev, 0x00, 0x01a7); + break; } - return 0; } static void setautogain(struct gspca_dev *gspca_dev) @@ -6222,45 +6103,46 @@ static void setautogain(struct gspca_dev *gspca_dev) autoval = 0x42; else autoval = 0x02; - reg_w(gspca_dev->dev, autoval, 0x0180); + reg_w(gspca_dev, autoval, 0x0180); } -static void send_unknown(struct usb_device *dev, int sensor) +static void send_unknown(struct gspca_dev *gspca_dev, int sensor) { - reg_w(dev, 0x01, 0x0000); /* led off */ + reg_w(gspca_dev, 0x01, 0x0000); /* led off */ switch (sensor) { case SENSOR_PAS106: - reg_w(dev, 0x03, 0x003a); - reg_w(dev, 0x0c, 0x003b); - reg_w(dev, 0x08, 0x0038); + reg_w(gspca_dev, 0x03, 0x003a); + reg_w(gspca_dev, 0x0c, 0x003b); + reg_w(gspca_dev, 0x08, 0x0038); break; case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_OV7620: - case SENSOR_MI0360SOC: + case SENSOR_MT9V111_1: + case SENSOR_MT9V111_3: case SENSOR_PB0330: case SENSOR_PO2030: - reg_w(dev, 0x0d, 0x003a); - reg_w(dev, 0x02, 0x003b); - reg_w(dev, 0x00, 0x0038); + reg_w(gspca_dev, 0x0d, 0x003a); + reg_w(gspca_dev, 0x02, 0x003b); + reg_w(gspca_dev, 0x00, 0x0038); break; case SENSOR_PAS202B: - reg_w(dev, 0x03, 0x003b); - reg_w(dev, 0x0c, 0x003a); - reg_w(dev, 0x0b, 0x0039); - reg_w(dev, 0x0b, 0x0038); + reg_w(gspca_dev, 0x03, 0x003b); + reg_w(gspca_dev, 0x0c, 0x003a); + reg_w(gspca_dev, 0x0b, 0x0039); + reg_w(gspca_dev, 0x0b, 0x0038); break; } } /* start probe 2 wires */ -static void start_2wr_probe(struct usb_device *dev, int sensor) +static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor) { - reg_w(dev, 0x01, 0x0000); - reg_w(dev, sensor, 0x0010); - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0x03, 0x0012); - reg_w(dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, sensor, 0x0010); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); /* msleep(2); */ } @@ -6268,14 +6150,14 @@ static int sif_probe(struct gspca_dev *gspca_dev) { u16 checkword; - start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */ - reg_w(gspca_dev->dev, 0x08, 0x008d); + start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */ + reg_w(gspca_dev, 0x08, 0x008d); msleep(150); checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4) | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4); PDEBUG(D_PROBE, "probe sif 0x%04x", checkword); if (checkword == 0x0007) { - send_unknown(gspca_dev->dev, SENSOR_PAS106); + send_unknown(gspca_dev, SENSOR_PAS106); return 0x0f; /* PAS106 */ } return -1; @@ -6283,23 +6165,22 @@ static int sif_probe(struct gspca_dev *gspca_dev) static int vga_2wr_probe(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; u16 retword; - start_2wr_probe(dev, 0x00); /* HV7131B */ + start_2wr_probe(gspca_dev, 0x00); /* HV7131B */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); retword = i2c_read(gspca_dev, 0x01); if (retword != 0) return 0x00; /* HV7131B */ - start_2wr_probe(dev, 0x04); /* CS2102 */ + start_2wr_probe(gspca_dev, 0x04); /* CS2102 */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); retword = i2c_read(gspca_dev, 0x01); if (retword != 0) return 0x04; /* CS2102 */ - start_2wr_probe(dev, 0x06); /* OmniVision */ - reg_w(dev, 0x08, 0x008d); + start_2wr_probe(gspca_dev, 0x06); /* OmniVision */ + reg_w(gspca_dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x11, 0xaa, 0x00); retword = i2c_read(gspca_dev, 0x11); if (retword != 0) { @@ -6308,14 +6189,14 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) goto ov_check; } - start_2wr_probe(dev, 0x08); /* HDCS2020 */ + start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */ i2c_write(gspca_dev, 0x1c, 0x00, 0x00); i2c_write(gspca_dev, 0x15, 0xaa, 0x00); retword = i2c_read(gspca_dev, 0x15); if (retword != 0) return 0x08; /* HDCS2020 */ - start_2wr_probe(dev, 0x0a); /* PB0330 */ + start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */ i2c_write(gspca_dev, 0x07, 0xaa, 0xaa); retword = i2c_read(gspca_dev, 0x07); if (retword != 0) @@ -6327,23 +6208,23 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) if (retword != 0) return 0x0a; /* PB0330 ?? */ - start_2wr_probe(dev, 0x0c); /* ICM105A */ + start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */ i2c_write(gspca_dev, 0x01, 0x11, 0x00); retword = i2c_read(gspca_dev, 0x01); if (retword != 0) return 0x0c; /* ICM105A */ - start_2wr_probe(dev, 0x0e); /* PAS202BCB */ - reg_w(dev, 0x08, 0x008d); + start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */ + reg_w(gspca_dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x03, 0xaa, 0x00); msleep(50); retword = i2c_read(gspca_dev, 0x03); if (retword != 0) { - send_unknown(dev, SENSOR_PAS202B); + send_unknown(gspca_dev, SENSOR_PAS202B); return 0x0e; /* PAS202BCB */ } - start_2wr_probe(dev, 0x02); /* TAS5130C */ + start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); retword = i2c_read(gspca_dev, 0x01); if (retword != 0) @@ -6352,20 +6233,20 @@ ov_check: reg_r(gspca_dev, 0x0010); /* ?? */ reg_r(gspca_dev, 0x0010); - reg_w(dev, 0x01, 0x0000); - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0x06, 0x0010); /* OmniVision */ - reg_w(dev, 0xa1, 0x008b); - reg_w(dev, 0x08, 0x008d); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */ + reg_w(gspca_dev, 0xa1, 0x008b); + reg_w(gspca_dev, 0x08, 0x008d); msleep(500); - reg_w(dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */ retword = i2c_read(gspca_dev, 0x0a) << 8; retword |= i2c_read(gspca_dev, 0x0b); PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword); switch (retword) { case 0x7631: /* OV7630C */ - reg_w(dev, 0x06, 0x0010); + reg_w(gspca_dev, 0x06, 0x0010); break; case 0x7620: /* OV7620 */ case 0x7648: /* OV7648 */ @@ -6382,32 +6263,31 @@ struct sensor_by_chipset_revision { }; static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { {0xc000, 0x12}, /* TAS5130C */ - {0xc001, 0x13}, /* MI0360SOC */ + {0xc001, 0x13}, /* MT9V111 */ {0xe001, 0x13}, {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ - {0x8400, 0x15}, /* TAS5130K */ + {0x8400, 0x15}, /* MT9V111 */ {0xe400, 0x15}, }; static int vga_3wr_probe(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int i; u8 retbyte; u16 retword; /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ - reg_w(dev, 0x02, 0x0010); + reg_w(gspca_dev, 0x02, 0x0010); reg_r(gspca_dev, 0x0010); - reg_w(dev, 0x01, 0x0000); - reg_w(dev, 0x00, 0x0010); - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0x91, 0x008b); - reg_w(dev, 0x03, 0x0012); - reg_w(dev, 0x01, 0x0012); - reg_w(dev, 0x05, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x00, 0x0010); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x91, 0x008b); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); retword = i2c_read(gspca_dev, 0x14); if (retword != 0) return 0x11; /* HV7131R */ @@ -6418,93 +6298,90 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) if (retword != 0) return 0x11; /* HV7131R */ - reg_w(dev, 0x02, 0x0010); + reg_w(gspca_dev, 0x02, 0x0010); retword = reg_r(gspca_dev, 0x000b) << 8; retword |= reg_r(gspca_dev, 0x000a); PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword); reg_r(gspca_dev, 0x0010); - /* value 0x4001 is meaningless */ - if (retword != 0x4001) { - if ((retword & 0xff00) == 0x6400) - return 0x02; /* TAS5130C */ - for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { - if (chipset_revision_sensor[i].revision == retword) { - sd->chip_revision = retword; - send_unknown(dev, SENSOR_PB0330); - return chipset_revision_sensor[i] - .internal_sensor_id; - } + if ((retword & 0xff00) == 0x6400) + return 0x02; /* TAS5130C */ + for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { + if (chipset_revision_sensor[i].revision == retword) { + sd->chip_revision = retword; + send_unknown(gspca_dev, SENSOR_PB0330); + return chipset_revision_sensor[i] + .internal_sensor_id; } } - reg_w(dev, 0x01, 0x0000); /* check PB0330 */ - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0xdd, 0x008b); - reg_w(dev, 0x0a, 0x0010); - reg_w(dev, 0x03, 0x0012); - reg_w(dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */ + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0xdd, 0x008b); + reg_w(gspca_dev, 0x0a, 0x0010); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); retword = i2c_read(gspca_dev, 0x00); if (retword != 0) { - PDEBUG(D_PROBE, "probe 3wr vga type 0a ?"); + PDEBUG(D_PROBE, "probe 3wr vga type 0a"); return 0x0a; /* PB0330 */ } - reg_w(dev, 0x01, 0x0000); - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0x98, 0x008b); - reg_w(dev, 0x01, 0x0010); - reg_w(dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x98, 0x008b); + reg_w(gspca_dev, 0x01, 0x0010); + reg_w(gspca_dev, 0x03, 0x0012); msleep(2); - reg_w(dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); retword = i2c_read(gspca_dev, 0x00); if (retword != 0) { PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword); if (retword == 0x0011) /* VF0250 */ return 0x0250; if (retword == 0x0029) /* gc0305 */ - send_unknown(dev, SENSOR_GC0305); + send_unknown(gspca_dev, SENSOR_GC0305); return retword; } - reg_w(dev, 0x01, 0x0000); /* check OmniVision */ - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0xa1, 0x008b); - reg_w(dev, 0x08, 0x008d); - reg_w(dev, 0x06, 0x0010); - reg_w(dev, 0x01, 0x0012); - reg_w(dev, 0x05, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */ + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0xa1, 0x008b); + reg_w(gspca_dev, 0x08, 0x008d); + reg_w(gspca_dev, 0x06, 0x0010); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */ && i2c_read(gspca_dev, 0x1d) == 0x00a2) { - send_unknown(dev, SENSOR_OV7620); + send_unknown(gspca_dev, SENSOR_OV7620); return 0x06; /* OmniVision confirm ? */ } - reg_w(dev, 0x01, 0x0000); - reg_w(dev, 0x00, 0x0002); - reg_w(dev, 0x01, 0x0010); - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0xee, 0x008b); - reg_w(dev, 0x03, 0x0012); - reg_w(dev, 0x01, 0x0012); - reg_w(dev, 0x05, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x00, 0x0002); + reg_w(gspca_dev, 0x01, 0x0010); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0xee, 0x008b); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */ PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword); if (retword == 0x2030) { retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); - send_unknown(dev, SENSOR_PO2030); + send_unknown(gspca_dev, SENSOR_PO2030); return retword; } - reg_w(dev, 0x01, 0x0000); - reg_w(dev, 0x0a, 0x0010); - reg_w(dev, 0xd3, 0x008b); - reg_w(dev, 0x01, 0x0001); - reg_w(dev, 0x03, 0x0012); - reg_w(dev, 0x01, 0x0012); - reg_w(dev, 0x05, 0x0012); - reg_w(dev, 0xd3, 0x008b); + reg_w(gspca_dev, 0x01, 0x0000); + reg_w(gspca_dev, 0x0a, 0x0010); + reg_w(gspca_dev, 0xd3, 0x008b); + reg_w(gspca_dev, 0x01, 0x0001); + reg_w(gspca_dev, 0x03, 0x0012); + reg_w(gspca_dev, 0x01, 0x0012); + reg_w(gspca_dev, 0x05, 0x0012); + reg_w(gspca_dev, 0xd3, 0x008b); retword = i2c_read(gspca_dev, 0x01); if (retword != 0) { PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword); @@ -6541,54 +6418,74 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; + + if (id->idProduct == 0x301b) + sd->bridge = BRIDGE_ZC301; + else + sd->bridge = BRIDGE_ZC303; + + /* define some sensors from the vendor/product */ + sd->sensor = id->driver_info; + + sd->sharpness = SHARPNESS_DEF; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->lightfreq = FREQ_DEF; + sd->quality = QUALITY_DEF; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; int sensor; static const u8 gamma[SENSOR_MAX] = { - 4, /* SENSOR_ADCM2700 0 */ - 4, /* SENSOR_CS2102 1 */ - 5, /* SENSOR_CS2102K 2 */ - 4, /* SENSOR_GC0305 3 */ - 4, /* SENSOR_HDCS2020b 4 */ - 4, /* SENSOR_HV7131B 5 */ - 4, /* SENSOR_HV7131C 6 */ - 4, /* SENSOR_ICM105A 7 */ - 4, /* SENSOR_MC501CB 8 */ - 4, /* SENSOR_MI0360SOC 9 */ - 3, /* SENSOR_OV7620 10 */ - 4, /* SENSOR_OV7630C 11 */ - 4, /* SENSOR_PAS106 12 */ - 4, /* SENSOR_PAS202B 13 */ - 4, /* SENSOR_PB0330 14 */ - 4, /* SENSOR_PO2030 15 */ - 4, /* SENSOR_TAS5130CK 16 */ - 3, /* SENSOR_TAS5130CXX 17 */ - 3, /* SENSOR_TAS5130C_VF0250 18 */ + [SENSOR_ADCM2700] = 4, + [SENSOR_CS2102] = 4, + [SENSOR_CS2102K] = 5, + [SENSOR_GC0305] = 4, + [SENSOR_HDCS2020b] = 4, + [SENSOR_HV7131B] = 4, + [SENSOR_HV7131R] = 4, + [SENSOR_ICM105A] = 4, + [SENSOR_MC501CB] = 4, + [SENSOR_MT9V111_1] = 4, + [SENSOR_MT9V111_3] = 4, + [SENSOR_OV7620] = 3, + [SENSOR_OV7630C] = 4, + [SENSOR_PAS106] = 4, + [SENSOR_PAS202B] = 4, + [SENSOR_PB0330] = 4, + [SENSOR_PO2030] = 4, + [SENSOR_TAS5130C] = 3, + [SENSOR_TAS5130C_VF0250] = 3, }; static const u8 mode_tb[SENSOR_MAX] = { - 2, /* SENSOR_ADCM2700 0 */ - 1, /* SENSOR_CS2102 1 */ - 1, /* SENSOR_CS2102K 2 */ - 1, /* SENSOR_GC0305 3 */ - 1, /* SENSOR_HDCS2020b 4 */ - 1, /* SENSOR_HV7131B 5 */ - 1, /* SENSOR_HV7131C 6 */ - 1, /* SENSOR_ICM105A 7 */ - 2, /* SENSOR_MC501CB 8 */ - 1, /* SENSOR_MI0360SOC 9 */ - 2, /* SENSOR_OV7620 10 */ - 1, /* SENSOR_OV7630C 11 */ - 0, /* SENSOR_PAS106 12 */ - 1, /* SENSOR_PAS202B 13 */ - 1, /* SENSOR_PB0330 14 */ - 1, /* SENSOR_PO2030 15 */ - 1, /* SENSOR_TAS5130CK 16 */ - 1, /* SENSOR_TAS5130CXX 17 */ - 1, /* SENSOR_TAS5130C_VF0250 18 */ + [SENSOR_ADCM2700] = 2, + [SENSOR_CS2102] = 1, + [SENSOR_CS2102K] = 1, + [SENSOR_GC0305] = 1, + [SENSOR_HDCS2020b] = 1, + [SENSOR_HV7131B] = 1, + [SENSOR_HV7131R] = 1, + [SENSOR_ICM105A] = 1, + [SENSOR_MC501CB] = 2, + [SENSOR_MT9V111_1] = 1, + [SENSOR_MT9V111_3] = 1, + [SENSOR_OV7620] = 2, + [SENSOR_OV7630C] = 1, + [SENSOR_PAS106] = 0, + [SENSOR_PAS202B] = 1, + [SENSOR_PB0330] = 1, + [SENSOR_PO2030] = 1, + [SENSOR_TAS5130C] = 1, + [SENSOR_TAS5130C_VF0250] = 1, }; - /* define some sensors from the vendor/product */ - sd->sharpness = SHARPNESS_DEF; - sd->sensor = id->driver_info; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) PDEBUG(D_PROBE, "probe sensor -> %04x", sensor); @@ -6607,8 +6504,8 @@ static int sd_config(struct gspca_dev *gspca_dev, break; default: PDEBUG(D_PROBE, - "Sensor UNKNOWN_0 force Tas5130"); - sd->sensor = SENSOR_TAS5130CXX; + "Unknown sensor - set to TAS5130C"); + sd->sensor = SENSOR_TAS5130C; } break; case 0: @@ -6623,14 +6520,14 @@ static int sd_config(struct gspca_dev *gspca_dev, break; default: /* case 2: * hv7131r */ - PDEBUG(D_PROBE, "Find Sensor HV7131R(c)"); - sd->sensor = SENSOR_HV7131C; + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; break; } break; case 0x02: PDEBUG(D_PROBE, "Sensor TAS5130C"); - sd->sensor = SENSOR_TAS5130CXX; + sd->sensor = SENSOR_TAS5130C; break; case 0x04: PDEBUG(D_PROBE, "Find Sensor CS2102"); @@ -6662,17 +6559,20 @@ static int sd_config(struct gspca_dev *gspca_dev, case 0x10: case 0x12: PDEBUG(D_PROBE, "Find Sensor TAS5130C"); - sd->sensor = SENSOR_TAS5130CXX; + sd->sensor = SENSOR_TAS5130C; break; case 0x11: - PDEBUG(D_PROBE, "Find Sensor HV7131R(c)"); - sd->sensor = SENSOR_HV7131C; + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; break; case 0x13: + case 0x15: PDEBUG(D_PROBE, - "Find Sensor MI0360SOC. Chip revision %x", + "Sensor MT9V111. Chip revision %04x", sd->chip_revision); - sd->sensor = SENSOR_MI0360SOC; + sd->sensor = sd->bridge == BRIDGE_ZC301 + ? SENSOR_MT9V111_1 + : SENSOR_MT9V111_3; break; case 0x14: PDEBUG(D_PROBE, @@ -6680,12 +6580,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->chip_revision); sd->sensor = SENSOR_CS2102K; break; - case 0x15: - PDEBUG(D_PROBE, - "Find Sensor TAS5130CK?. Chip revision %x", - sd->chip_revision); - sd->sensor = SENSOR_TAS5130CK; - break; case 0x16: PDEBUG(D_PROBE, "Find Sensor ADCM2700"); sd->sensor = SENSOR_ADCM2700; @@ -6722,13 +6616,11 @@ static int sd_config(struct gspca_dev *gspca_dev, } if (sensor < 0x20) { if (sensor == -1 || sensor == 0x10 || sensor == 0x12) - reg_w(gspca_dev->dev, 0x02, 0x0010); + reg_w(gspca_dev, 0x02, 0x0010); reg_r(gspca_dev, 0x0010); } cam = &gspca_dev->cam; -/*fixme:test*/ - gspca_dev->nbalt--; switch (mode_tb[sd->sensor]) { case 0: cam->cam_mode = sif_mode; @@ -6744,70 +6636,72 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(broken_vga_mode); break; } - sd->contrast = CONTRAST_DEF; sd->gamma = gamma[sd->sensor]; - sd->autogain = AUTOGAIN_DEF; - sd->lightfreq = FREQ_DEF; - sd->quality = QUALITY_DEF; switch (sd->sensor) { - case SENSOR_HV7131B: - case SENSOR_HV7131C: case SENSOR_OV7630C: gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); break; } - return 0; -} - -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) -{ /* switch off the led */ - reg_w(gspca_dev->dev, 0x01, 0x0000); - return 0; + reg_w(gspca_dev, 0x01, 0x0000); + return gspca_dev->usb_err; } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int mode; static const struct usb_action *init_tb[SENSOR_MAX][2] = { - {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */ - {cs2102_Initial, cs2102_InitialScale}, /* 1 */ - {cs2102K_Initial, cs2102K_InitialScale}, /* 2 */ - {gc0305_Initial, gc0305_InitialScale}, /* 3 */ - {hdcs2020b_Initial, hdcs2020b_InitialScale}, /* 4 */ - {hv7131b_Initial, hv7131b_InitialScale}, /* 5 */ - {hv7131r_Initial, hv7131r_InitialScale}, /* 6 */ - {icm105a_Initial, icm105a_InitialScale}, /* 7 */ - {mc501cb_Initial, mc501cb_InitialScale}, /* 8 */ - {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */ - {ov7620_Initial, ov7620_InitialScale}, /* 10 */ - {ov7630c_Initial, ov7630c_InitialScale}, /* 11 */ - {pas106b_Initial, pas106b_InitialScale}, /* 12 */ - {pas202b_Initial, pas202b_InitialScale}, /* 13 */ - {pb0330_Initial, pb0330_InitialScale}, /* 14 */ - {po2030_Initial, po2030_InitialScale}, /* 15 */ - {tas5130cK_Initial, tas5130cK_InitialScale}, /* 16 */ - {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */ + [SENSOR_ADCM2700] = + {adcm2700_Initial, adcm2700_InitialScale}, + [SENSOR_CS2102] = + {cs2102_Initial, cs2102_InitialScale}, + [SENSOR_CS2102K] = + {cs2102K_Initial, cs2102K_InitialScale}, + [SENSOR_GC0305] = + {gc0305_Initial, gc0305_InitialScale}, + [SENSOR_HDCS2020b] = + {hdcs2020b_Initial, hdcs2020b_InitialScale}, + [SENSOR_HV7131B] = + {hv7131b_Initial, hv7131b_InitialScale}, + [SENSOR_HV7131R] = + {hv7131r_Initial, hv7131r_InitialScale}, + [SENSOR_ICM105A] = + {icm105a_Initial, icm105a_InitialScale}, + [SENSOR_MC501CB] = + {mc501cb_Initial, mc501cb_InitialScale}, + [SENSOR_MT9V111_1] = + {mt9v111_1_Initial, mt9v111_1_InitialScale}, + [SENSOR_MT9V111_3] = + {mt9v111_3_Initial, mt9v111_3_InitialScale}, + [SENSOR_OV7620] = + {ov7620_Initial, ov7620_InitialScale}, + [SENSOR_OV7630C] = + {ov7630c_Initial, ov7630c_InitialScale}, + [SENSOR_PAS106] = + {pas106b_Initial, pas106b_InitialScale}, + [SENSOR_PAS202B] = + {pas202b_Initial, pas202b_InitialScale}, + [SENSOR_PB0330] = + {pb0330_Initial, pb0330_InitialScale}, + [SENSOR_PO2030] = + {po2030_Initial, po2030_InitialScale}, + [SENSOR_TAS5130C] = + {tas5130c_Initial, tas5130c_InitialScale}, + [SENSOR_TAS5130C_VF0250] = {tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale}, - /* 18 */ }; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->sensor) { - case SENSOR_HV7131C: + case SENSOR_HV7131R: zcxx_probeSensor(gspca_dev); break; case SENSOR_PAS106: @@ -6821,22 +6715,22 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_GC0305: case SENSOR_OV7620: case SENSOR_PO2030: - case SENSOR_TAS5130CXX: + case SENSOR_TAS5130C: case SENSOR_TAS5130C_VF0250: /* msleep(100); * ?? */ reg_r(gspca_dev, 0x0002); /* --> 0x40 */ - reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(dev, 0x15, 0x01ae); - if (sd->sensor == SENSOR_TAS5130CXX) + reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(gspca_dev, 0x15, 0x01ae); + if (sd->sensor == SENSOR_TAS5130C) break; - reg_w(dev, 0x0d, 0x003a); - reg_w(dev, 0x02, 0x003b); - reg_w(dev, 0x00, 0x0038); + reg_w(gspca_dev, 0x0d, 0x003a); + reg_w(gspca_dev, 0x02, 0x003b); + reg_w(gspca_dev, 0x00, 0x0038); break; case SENSOR_PAS202B: - reg_w(dev, 0x03, 0x003b); - reg_w(dev, 0x0c, 0x003a); - reg_w(dev, 0x0b, 0x0039); + reg_w(gspca_dev, 0x03, 0x003b); + reg_w(gspca_dev, 0x0c, 0x003a); + reg_w(gspca_dev, 0x0b, 0x0039); break; } @@ -6845,15 +6739,15 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_ADCM2700: case SENSOR_OV7620: reg_r(gspca_dev, 0x0008); - reg_w(dev, 0x00, 0x0008); + reg_w(gspca_dev, 0x00, 0x0008); break; case SENSOR_PAS202B: case SENSOR_GC0305: - case SENSOR_TAS5130CXX: + case SENSOR_TAS5130C: reg_r(gspca_dev, 0x0008); /* fall thru */ case SENSOR_PO2030: - reg_w(dev, 0x03, 0x0008); + reg_w(gspca_dev, 0x03, 0x0008); break; } setsharpness(gspca_dev); @@ -6863,7 +6757,6 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_CS2102K: /* gamma set in xxx_Initial */ case SENSOR_HDCS2020b: case SENSOR_OV7630C: - case SENSOR_TAS5130CK: break; default: setcontrast(gspca_dev); @@ -6874,7 +6767,7 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7620: case SENSOR_PAS202B: reg_r(gspca_dev, 0x0180); /* from win */ - reg_w(dev, 0x00, 0x0180); + reg_w(gspca_dev, 0x00, 0x0180); break; default: setquality(gspca_dev); @@ -6884,29 +6777,29 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_ADCM2700: - reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(dev, 0x15, 0x01ae); - reg_w(dev, 0x02, 0x0180); + reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(gspca_dev, 0x15, 0x01ae); + reg_w(gspca_dev, 0x02, 0x0180); /* ms-win + */ - reg_w(dev, 0x40, 0x0117); + reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_GC0305: - case SENSOR_TAS5130CXX: - reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(dev, 0x15, 0x01ae); + case SENSOR_TAS5130C: + reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(gspca_dev, 0x15, 0x01ae); /* fall thru */ case SENSOR_PAS202B: case SENSOR_PO2030: -/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */ +/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */ reg_r(gspca_dev, 0x0180); break; case SENSOR_OV7620: - reg_w(dev, 0x09, 0x01ad); - reg_w(dev, 0x15, 0x01ae); + reg_w(gspca_dev, 0x09, 0x01ad); + reg_w(gspca_dev, 0x15, 0x01ae); i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */ i2c_write(gspca_dev, 0x13, 0xa3, 0x00); /*fixme: returned value to send? */ - reg_w(dev, 0x40, 0x0117); + reg_w(gspca_dev, 0x40, 0x0117); reg_r(gspca_dev, 0x0180); break; } @@ -6915,15 +6808,11 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_PO2030: msleep(50); - reg_w(dev, 0x00, 0x0007); /* (from win traces) */ - reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); - break; - case SENSOR_PAS202B: - reg_w(dev, 0x32, 0x0007); /* (from win traces) */ - reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); + reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */ + reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING); break; } - return 0; + return gspca_dev->usb_err; } /* called on streamoff with alt 0 and on disconnect */ @@ -6931,10 +6820,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - kfree(sd->jpeg_hdr); if (!gspca_dev->present) return; - send_unknown(gspca_dev->dev, sd->sensor); + send_unknown(gspca_dev, sd->sensor); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -6962,6 +6850,24 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -6969,7 +6875,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = val; if (gspca_dev->streaming) setcontrast(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) @@ -6987,7 +6893,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) sd->autogain = val; if (gspca_dev->streaming) setautogain(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) @@ -7005,7 +6911,7 @@ static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) sd->gamma = val; if (gspca_dev->streaming) setcontrast(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) @@ -7023,7 +6929,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) sd->lightfreq = val; if (gspca_dev->streaming) setlightfreq(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) @@ -7041,7 +6947,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) sd->sharpness = val; if (gspca_dev->streaming) setsharpness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) @@ -7086,7 +6992,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev, sd->quality = jcomp->quality; if (gspca_dev->streaming) jpeg_set_qual(sd->jpeg_hdr, sd->quality); - return 0; + return gspca_dev->usb_err; } static int sd_get_jcomp(struct gspca_dev *gspca_dev, @@ -7163,9 +7069,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08aa)}, {USB_DEVICE(0x046d, 0x08ac)}, {USB_DEVICE(0x046d, 0x08ad)}, -#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x046d, 0x08ae)}, -#endif {USB_DEVICE(0x046d, 0x08af)}, {USB_DEVICE(0x046d, 0x08b9)}, {USB_DEVICE(0x046d, 0x08d7)}, @@ -7192,7 +7096,6 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x10fd, 0x8050)}, {} /* end of entry */ }; -#undef DVNAME MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 830d47b05e1..0cae5b82e1a 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -286,6 +286,8 @@ static int hdpvr_probe(struct usb_interface *interface, goto error; } + dev->workqueue = 0; + /* register v4l2_device early so it can be used for printks */ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { err("v4l2_device_register failed"); @@ -380,6 +382,9 @@ static int hdpvr_probe(struct usb_interface *interface, error: if (dev) { + /* Destroy single thread */ + if (dev->workqueue) + destroy_workqueue(dev->workqueue); /* this frees allocated memory */ hdpvr_delete(dev); } diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index c338f3f62e7..4863a21b1f2 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -394,7 +394,7 @@ err: static int hdpvr_release(struct file *file) { - struct hdpvr_fh *fh = (struct hdpvr_fh *)file->private_data; + struct hdpvr_fh *fh = file->private_data; struct hdpvr_device *dev = fh->dev; if (!dev) @@ -518,7 +518,7 @@ err: static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) { struct hdpvr_buffer *buf = NULL; - struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data; + struct hdpvr_fh *fh = filp->private_data; struct hdpvr_device *dev = fh->dev; unsigned int mask = 0; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 29d43974265..27ae8bbfb47 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -47,7 +47,7 @@ #include <linux/i2c-id.h> #include <linux/workqueue.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include <media/ir-kbd-i2c.h> /* ----------------------------------------------------------------------- */ @@ -272,11 +272,8 @@ static void ir_key_poll(struct IR_i2c *ir) return; } - if (0 == rc) { - ir_input_nokey(ir->input, &ir->ir); - } else { - ir_input_keydown(ir->input, &ir->ir, ir_key); - } + if (rc) + ir_keydown(ir->input, ir_key, 0); } static void ir_work(struct work_struct *work) @@ -439,10 +436,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_name(&client->dev)); /* init + register input device */ - err = ir_input_init(input_dev, &ir->ir, ir_type); - if (err < 0) - goto err_out_free; - + ir->ir_type = ir_type; input_dev->id.bustype = BUS_I2C; input_dev->name = ir->name; input_dev->phys = ir->phys; diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig index c46bfb1569e..be4af1fa557 100644 --- a/drivers/media/video/ivtv/Kconfig +++ b/drivers/media/video/ivtv/Kconfig @@ -3,7 +3,7 @@ config VIDEO_IVTV depends on VIDEO_V4L2 && PCI && I2C depends on INPUT # due to VIDEO_IR select I2C_ALGOBIT - select VIDEO_IR + depends on VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index b588e30cbcf..b31ee1bceef 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -17,163 +17,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/kernel.h> -#include <linux/slab.h> #include "ivtv-driver.h" -#include "ivtv-cards.h" #include "ivtv-ioctl.h" -#include "ivtv-routing.h" -#include "ivtv-i2c.h" -#include "ivtv-mailbox.h" #include "ivtv-controls.h" -/* Must be sorted from low to high control ID! */ -static const u32 user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_AUDIO_BALANCE, - V4L2_CID_AUDIO_BASS, - V4L2_CID_AUDIO_TREBLE, - V4L2_CID_AUDIO_MUTE, - V4L2_CID_AUDIO_LOUDNESS, - 0 -}; - -static const u32 *ctrl_classes[] = { - user_ctrls, - cx2341x_mpeg_ctrls, - NULL -}; - - -int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) -{ - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - const char *name; - - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - - switch (qctrl->id) { - /* Standard V4L2 controls */ - case V4L2_CID_USER_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_LOUDNESS: - if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - - default: - if (cx2341x_ctrl_query(&itv->params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - } - strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); - qctrl->name[sizeof(qctrl->name) - 1] = 0; - return 0; -} - -int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) -{ - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - struct v4l2_queryctrl qctrl; - - qctrl.id = qmenu->id; - ivtv_queryctrl(file, fh, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(&itv->params, qmenu->id)); -} - -static int ivtv_try_ctrl(struct file *file, void *fh, - struct v4l2_ext_control *vctrl) -{ - struct v4l2_queryctrl qctrl; - const char **menu_items = NULL; - int err; - - qctrl.id = vctrl->id; - err = ivtv_queryctrl(file, fh, &qctrl); - if (err) - return err; - if (qctrl.type == V4L2_CTRL_TYPE_MENU) - menu_items = v4l2_ctrl_get_menu(qctrl.id); - return v4l2_ctrl_check(vctrl, &qctrl, menu_items); -} - -static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) -{ - switch (vctrl->id) { - /* Standard V4L2 controls */ - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl); - - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl); - - default: - IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); - return -EINVAL; - } - return 0; -} - -static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) { - switch (vctrl->id) { - /* Standard V4L2 controls */ - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl); - - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl); - default: - IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); - return -EINVAL; - } - return 0; -} - -static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt) -{ - if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) - return -EINVAL; - if (atomic_read(&itv->capturing) > 0) - return -EBUSY; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); /* First try to allocate sliced VBI buffers if needed. */ if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { @@ -208,106 +59,43 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm return 0; } -int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - struct v4l2_control ctrl; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = ivtv_g_ctrl(itv, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } - } - return err; - } - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS); - return -EINVAL; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); + int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_mbus_framefmt fmt; + + /* fix videodecoder resolution */ + fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); + fmt.height = cxhdl->height; + fmt.code = V4L2_MBUS_FMT_FIXED; + v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt); + return 0; } -int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - struct v4l2_control ctrl; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = ivtv_s_ctrl(itv, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } - } - return err; - } - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - static u32 freqs[3] = { 44100, 48000, 32000 }; - struct cx2341x_mpeg_params p = itv->params; - int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS); - unsigned idx; - - if (err) - return err; + static const u32 freqs[3] = { 44100, 48000, 32000 }; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); - if (p.video_encoding != itv->params.video_encoding) { - int is_mpeg1 = p.video_encoding == - V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_mbus_framefmt fmt; - - /* fix videodecoder resolution */ - fmt.width = itv->params.width / (is_mpeg1 ? 2 : 1); - fmt.height = itv->params.height; - fmt.code = V4L2_MBUS_FMT_FIXED; - v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt); - } - err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); - if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) - err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); - itv->params = p; - itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; - idx = p.audio_properties & 0x03; - /* The audio clock of the digitizer must match the codec sample - rate otherwise you get some very strange effects. */ - if (idx < ARRAY_SIZE(freqs)) - ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); - return err; - } - return -EINVAL; + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (idx < ARRAY_SIZE(freqs)) + ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); + return 0; } -int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val) { - struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - err = ivtv_try_ctrl(file, fh, &c->controls[i]); - if (err) { - c->error_idx = i; - break; - } - } - return err; - } - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS); - return -EINVAL; + itv->dualwatch_stereo_mode = val; + return 0; } + +struct cx2341x_handler_ops ivtv_cxhdl_ops = { + .s_audio_mode = ivtv_s_audio_mode, + .s_audio_sampling_freq = ivtv_s_audio_sampling_freq, + .s_video_encoding = ivtv_s_video_encoding, + .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt, +}; diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h index 1c7721e23c9..d12893dd018 100644 --- a/drivers/media/video/ivtv/ivtv-controls.h +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -21,10 +21,6 @@ #ifndef IVTV_CONTROLS_H #define IVTV_CONTROLS_H -int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); -int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); -int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); -int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); -int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a); +extern struct cx2341x_handler_ops ivtv_cxhdl_ops; #endif diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 1b79475ca13..e421d15b0f5 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -53,6 +53,7 @@ #include "ivtv-cards.h" #include "ivtv-vbi.h" #include "ivtv-routing.h" +#include "ivtv-controls.h" #include "ivtv-gpio.h" #include <media/tveeprom.h> @@ -130,6 +131,9 @@ static int ivtv_yuv_threshold = -1; static int ivtv_pci_latency = 1; int ivtv_debug; +#ifdef CONFIG_VIDEO_ADV_DEBUG +int ivtv_fw_debug; +#endif static int tunertype = -1; static int newi2c = -1; @@ -141,6 +145,9 @@ module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); module_param_named(debug,ivtv_debug, int, 0644); +#ifdef CONFIG_VIDEO_ADV_DEBUG +module_param_named(fw_debug, ivtv_fw_debug, int, 0644); +#endif module_param(ivtv_pci_latency, int, 0644); module_param(ivtv_yuv_mode, int, 0644); module_param(ivtv_yuv_threshold, int, 0644); @@ -217,6 +224,10 @@ MODULE_PARM_DESC(debug, "\t\t\t 256/0x0100: yuv\n" "\t\t\t 512/0x0200: i2c\n" "\t\t\t1024/0x0400: high volume\n"); +#ifdef CONFIG_VIDEO_ADV_DEBUG +MODULE_PARM_DESC(fw_debug, + "Enable code for debugging firmware problems. Default: 0\n"); +#endif MODULE_PARM_DESC(ivtv_pci_latency, "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" "\t\t\tDefault: Yes"); @@ -695,6 +706,8 @@ done: */ static int __devinit ivtv_init_struct1(struct ivtv *itv) { + struct sched_param param = { .sched_priority = 99 }; + itv->base_addr = pci_resource_start(itv->pdev, 0); itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ @@ -706,21 +719,24 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_singlethread_workqueue(itv->v4l2_dev.name); - if (itv->irq_work_queues == NULL) { - IVTV_ERR("Could not create ivtv workqueue\n"); + init_kthread_worker(&itv->irq_worker); + itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker, + itv->v4l2_dev.name); + if (IS_ERR(itv->irq_worker_task)) { + IVTV_ERR("Could not create ivtv task\n"); return -1; } + /* must use the FIFO scheduler as it is realtime sensitive */ + sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, ¶m); - INIT_WORK(&itv->irq_work_queue, ivtv_irq_work_handler); + init_kthread_work(&itv->irq_work, ivtv_irq_work_handler); /* start counting open_id at 1 */ itv->open_id = 1; /* Initial settings */ - cx2341x_fill_defaults(&itv->params); - itv->params.port = CX2341X_PORT_MEMORY; - itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI; + itv->cxhdl.port = CX2341X_PORT_MEMORY; + itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI; init_waitqueue_head(&itv->eos_waitq); init_waitqueue_head(&itv->event_waitq); init_waitqueue_head(&itv->vsync_waitq); @@ -990,13 +1006,20 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, retval = -ENOMEM; goto err; } + retval = cx2341x_handler_init(&itv->cxhdl, 50); + if (retval) + goto err; + itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl; + itv->cxhdl.ops = &ivtv_cxhdl_ops; + itv->cxhdl.priv = itv; + itv->cxhdl.func = ivtv_api_func; IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr); /* PCI Device Setup */ retval = ivtv_setup_pci(itv, pdev, pci_id); if (retval == -EIO) - goto free_workqueue; + goto free_worker; if (retval == -ENXIO) goto free_mem; @@ -1111,7 +1134,7 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w; itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h; - itv->params.video_gop_size = itv->is_60hz ? 15 : 12; + cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz); itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200; @@ -1208,8 +1231,8 @@ free_mem: release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); if (itv->has_cx23415) release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); -free_workqueue: - destroy_workqueue(itv->irq_work_queues); +free_worker: + kthread_stop(itv->irq_worker_task); err: if (retval == 0) retval = -ENODEV; @@ -1253,15 +1276,8 @@ int ivtv_init_on_first_open(struct ivtv *itv) IVTV_DEBUG_INFO("Getting firmware version..\n"); ivtv_firmware_versions(itv); - if (itv->card->hw_all & IVTV_HW_CX25840) { - struct v4l2_control ctrl; - + if (itv->card->hw_all & IVTV_HW_CX25840) v4l2_subdev_call(itv->sd_video, core, load_fw); - /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ - ctrl.id = V4L2_CID_PRIVATE_BASE; - ctrl.value = itv->pvr150_workaround; - v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl); - } vf.tuner = 0; vf.type = V4L2_TUNER_ANALOG_TV; @@ -1313,6 +1329,8 @@ int ivtv_init_on_first_open(struct ivtv *itv) /* For cards with video out, this call needs interrupts enabled */ ivtv_s_std(NULL, &fh, &itv->tuner_std); + /* Setup initial controls */ + cx2341x_handler_setup(&itv->cxhdl); return 0; } @@ -1353,9 +1371,9 @@ static void ivtv_remove(struct pci_dev *pdev) ivtv_set_irq_mask(itv, 0xffffffff); del_timer_sync(&itv->dma_timer); - /* Stop all Work Queues */ - flush_workqueue(itv->irq_work_queues); - destroy_workqueue(itv->irq_work_queues); + /* Kill irq worker */ + flush_kthread_worker(&itv->irq_worker); + kthread_stop(itv->irq_worker_task); ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); @@ -1425,12 +1443,16 @@ EXPORT_SYMBOL(ivtv_vapi); EXPORT_SYMBOL(ivtv_vapi_result); EXPORT_SYMBOL(ivtv_clear_irq_mask); EXPORT_SYMBOL(ivtv_debug); +#ifdef CONFIG_VIDEO_ADV_DEBUG +EXPORT_SYMBOL(ivtv_fw_debug); +#endif EXPORT_SYMBOL(ivtv_reset_ir_gpio); EXPORT_SYMBOL(ivtv_udma_setup); EXPORT_SYMBOL(ivtv_udma_unmap); EXPORT_SYMBOL(ivtv_udma_alloc); EXPORT_SYMBOL(ivtv_udma_prepare); EXPORT_SYMBOL(ivtv_init_on_first_open); +EXPORT_SYMBOL(ivtv_firmware_check); module_init(module_start); module_exit(module_cleanup); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 5b45fd2b264..75803141481 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -51,7 +51,7 @@ #include <linux/unistd.h> #include <linux/pagemap.h> #include <linux/scatterlist.h> -#include <linux/workqueue.h> +#include <linux/kthread.h> #include <linux/mutex.h> #include <linux/slab.h> #include <asm/uaccess.h> @@ -62,6 +62,7 @@ #include <linux/dvb/audio.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fh.h> #include <media/tuner.h> @@ -122,6 +123,9 @@ /* debugging */ extern int ivtv_debug; +#ifdef CONFIG_VIDEO_ADV_DEBUG +extern int ivtv_fw_debug; +#endif #define IVTV_DBGFLG_WARN (1 << 0) #define IVTV_DBGFLG_INFO (1 << 1) @@ -257,7 +261,6 @@ struct ivtv_mailbox_data { #define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */ #define IVTV_F_I_INITED 21 /* set after first open */ #define IVTV_F_I_FAILED 22 /* set if first open failed */ -#define IVTV_F_I_WORK_INITED 23 /* worker thread was initialized */ /* Event notifications */ #define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ @@ -629,6 +632,8 @@ struct ivtv { struct ivtv_options options; /* user options */ struct v4l2_device v4l2_dev; + struct cx2341x_handler cxhdl; + struct v4l2_ctrl_handler hdl_gpio; struct v4l2_subdev sd_gpio; /* GPIO sub-device */ u16 instance; @@ -646,7 +651,6 @@ struct ivtv { v4l2_std_id std_out; /* current TV output standard */ u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */ u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */ - struct cx2341x_mpeg_params params; /* current encoder parameters */ /* Locking */ @@ -663,8 +667,9 @@ struct ivtv { /* Interrupts & DMA */ u32 irqmask; /* active interrupts */ u32 irq_rr_idx; /* round-robin stream index */ - struct workqueue_struct *irq_work_queues; /* workqueue for PIO/YUV/VBI actions */ - struct work_struct irq_work_queue; /* work entry */ + struct kthread_worker irq_worker; /* kthread worker for PIO/YUV/VBI actions */ + struct task_struct *irq_worker_task; /* task for irq_worker */ + struct kthread_work irq_work; /* kthread work entry */ spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ int cur_dma_stream; /* index of current stream doing DMA (-1 if none) */ int cur_pio_stream; /* index of current stream doing PIO (-1 if none) */ @@ -734,6 +739,7 @@ struct ivtv { struct v4l2_rect osd_rect; /* current OSD position and size */ struct v4l2_rect main_rect; /* current Main window position and size */ struct osd_info *osd_info; /* ivtvfb private OSD info */ + void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */ }; static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev) diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 3c2cc270ccd..d727485da88 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -32,6 +32,7 @@ #include "ivtv-yuv.h" #include "ivtv-ioctl.h" #include "ivtv-cards.h" +#include "ivtv-firmware.h" #include <media/v4l2-event.h> #include <media/saa7115.h> @@ -149,12 +150,10 @@ void ivtv_release_stream(struct ivtv_stream *s) static void ivtv_dualwatch(struct ivtv *itv) { struct v4l2_tuner vt; - u32 new_bitmap; u32 new_stereo_mode; - const u32 stereo_mask = 0x0300; - const u32 dual = 0x0200; + const u32 dual = 0x02; - new_stereo_mode = itv->params.audio_properties & stereo_mask; + new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode); memset(&vt, 0, sizeof(vt)); ivtv_call_all(itv, tuner, g_tuner, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) @@ -163,16 +162,10 @@ static void ivtv_dualwatch(struct ivtv *itv) if (new_stereo_mode == itv->dualwatch_stereo_mode) return; - new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask); - - IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", - itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); - - if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) { - itv->dualwatch_stereo_mode = new_stereo_mode; - return; - } - IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); + IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n", + itv->dualwatch_stereo_mode, new_stereo_mode); + if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode)) + IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); } static void ivtv_update_pgm_info(struct ivtv *itv) @@ -526,6 +519,7 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) { struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; + int rc; if (atomic_read(&itv->decoding) == 0) { if (ivtv_claim_stream(id, s->type)) { @@ -533,7 +527,13 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) IVTV_DEBUG_WARN("start decode, stream already claimed\n"); return -EBUSY; } - ivtv_start_v4l2_decode_stream(s, 0); + rc = ivtv_start_v4l2_decode_stream(s, 0); + if (rc < 0) { + if (rc == -EAGAIN) + rc = ivtv_start_v4l2_decode_stream(s, 0); + if (rc < 0) + return rc; + } } if (s->type == IVTV_DEC_STREAM_TYPE_MPG) return ivtv_set_speed(itv, speed); @@ -886,7 +886,8 @@ int ivtv_v4l2_close(struct file *filp) if (atomic_read(&itv->capturing) > 0) { /* Undo video mute */ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, - itv->params.video_mute | (itv->params.video_mute_yuv << 8)); + v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) | + (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); } /* Done! Unmute and continue. */ ivtv_unmute(itv); @@ -912,12 +913,32 @@ int ivtv_v4l2_close(struct file *filp) static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) { +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct video_device *vdev = video_devdata(filp); +#endif struct ivtv *itv = s->itv; struct ivtv_open_id *item; int res = 0; IVTV_DEBUG_FILE("open %s\n", s->name); +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* Unless ivtv_fw_debug is set, error out if firmware dead. */ + if (ivtv_fw_debug) { + IVTV_WARN("Opening %s with dead firmware lockout disabled\n", + video_device_node_name(vdev)); + IVTV_WARN("Selected firmware errors will be ignored\n"); + } else { +#else + if (1) { +#endif + res = ivtv_firmware_check(itv, "ivtv_serialized_open"); + if (res == -EAGAIN) + res = ivtv_firmware_check(itv, "ivtv_serialized_open"); + if (res < 0) + return -EIO; + } + if (s->type == IVTV_DEC_STREAM_TYPE_MPG && test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) return -EBUSY; diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index a71e8ba306b..4df01947a7d 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.c +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -23,7 +23,10 @@ #include "ivtv-mailbox.h" #include "ivtv-firmware.h" #include "ivtv-yuv.h" +#include "ivtv-ioctl.h" +#include "ivtv-cards.h" #include <linux/firmware.h> +#include <media/saa7127.h> #define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE #define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 @@ -245,9 +248,9 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv) volatile u8 __iomem *mem_offset; data[0] = 0; - data[1] = itv->params.width; /* YUV source width */ - data[2] = itv->params.height; - data[3] = itv->params.audio_properties; /* Audio settings to use, + data[1] = itv->cxhdl.width; /* YUV source width */ + data[2] = itv->cxhdl.height; + data[3] = itv->cxhdl.audio_properties; /* Audio settings to use, bitmap. see docs. */ if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); @@ -271,3 +274,122 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv) } ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); } + +/* Try to restart the card & restore previous settings */ +int ivtv_firmware_restart(struct ivtv *itv) +{ + int rc = 0; + v4l2_std_id std; + struct ivtv_open_id fh; + fh.itv = itv; + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) + /* Display test image during restart */ + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, + SAA7127_INPUT_TYPE_TEST_IMAGE, + itv->card->video_outputs[itv->active_output].video_output, + 0); + + mutex_lock(&itv->udma.lock); + + rc = ivtv_firmware_init(itv); + if (rc) { + mutex_unlock(&itv->udma.lock); + return rc; + } + + /* Allow settings to reload */ + ivtv_mailbox_cache_invalidate(itv); + + /* Restore video standard */ + std = itv->std; + itv->std = 0; + ivtv_s_std(NULL, &fh, &std); + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_init_mpeg_decoder(itv); + + /* Restore framebuffer if active */ + if (itv->ivtvfb_restore) + itv->ivtvfb_restore(itv); + + /* Restore alpha settings */ + ivtv_set_osd_alpha(itv); + + /* Restore normal output */ + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, + SAA7127_INPUT_TYPE_NORMAL, + itv->card->video_outputs[itv->active_output].video_output, + 0); + } + + mutex_unlock(&itv->udma.lock); + return rc; +} + +/* Check firmware running state. The checks fall through + allowing multiple failures to be logged. */ +int ivtv_firmware_check(struct ivtv *itv, char *where) +{ + int res = 0; + + /* Check encoder is still running */ + if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { + IVTV_WARN("Encoder has died : %s\n", where); + res = -1; + } + + /* Also check audio. Only check if not in use & encoder is okay */ + if (!res && !atomic_read(&itv->capturing) && + (!atomic_read(&itv->decoding) || + (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, + &itv->i_flags)))) { + + if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { + IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); + res = -2; + } + } + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + /* Second audio check. Skip if audio already failed */ + if (res != -2 && read_dec(0x100) != read_dec(0x104)) { + /* Wait & try again to be certain. */ + ivtv_msleep_timeout(14, 0); + if (read_dec(0x100) != read_dec(0x104)) { + IVTV_WARN("Audio has died (Decoder) : %s\n", + where); + res = -1; + } + } + + /* Check decoder is still running */ + if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { + IVTV_WARN("Decoder has died : %s\n", where); + res = -1; + } + } + + /* If something failed & currently idle, try to reload */ + if (res && !atomic_read(&itv->capturing) && + !atomic_read(&itv->decoding)) { + IVTV_INFO("Detected in %s that firmware had failed - " + "Reloading\n", where); + res = ivtv_firmware_restart(itv); + /* + * Even if restarted ok, still signal a problem had occured. + * The caller can come through this function again to check + * if things are really ok after the restart. + */ + if (!res) { + IVTV_INFO("Firmware restart okay\n"); + res = -EAGAIN; + } else { + IVTV_INFO("Firmware restart failed\n"); + } + } else if (res) { + res = -EIO; + } + + return res; +} diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h index 041ba94e65b..52bb4e5598f 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.h +++ b/drivers/media/video/ivtv/ivtv-firmware.h @@ -26,5 +26,6 @@ int ivtv_firmware_init(struct ivtv *itv); void ivtv_firmware_versions(struct ivtv *itv); void ivtv_halt_firmware(struct ivtv *itv); void ivtv_init_mpeg_decoder(struct ivtv *itv); +int ivtv_firmware_check(struct ivtv *itv, char *where); #endif diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index aede061cae5..8f0d0778905 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -24,6 +24,7 @@ #include "ivtv-gpio.h" #include "tuner-xc2028.h" #include <media/tuner.h> +#include <media/v4l2-ctrls.h> /* * GPIO assignment of Yuan MPG600/MPG160 @@ -149,16 +150,10 @@ static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd) return container_of(sd, struct ivtv, sd_gpio); } -static struct v4l2_queryctrl gpio_ctrl_mute = { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, -}; +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio; +} static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { @@ -262,40 +257,24 @@ static int subdev_s_audio_routing(struct v4l2_subdev *sd, return 0; } -static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int subdev_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = itv->card->gpio_audio_mute.mute; - ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; - return 0; -} - -static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct ivtv *itv = sd_to_ivtv(sd); - u16 mask, data; - - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; - if (mask) - write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); - return 0; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | + (data & mask), IVTV_REG_GPIO_OUT); + return 0; + } + return -EINVAL; } -static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - if (qc->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - *qc = gpio_ctrl_mute; - return 0; -} static int subdev_log_status(struct v4l2_subdev *sd) { @@ -304,6 +283,7 @@ static int subdev_log_status(struct v4l2_subdev *sd) IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), read_reg(IVTV_REG_GPIO_IN)); + v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name); return 0; } @@ -327,11 +307,19 @@ static int subdev_s_video_routing(struct v4l2_subdev *sd, return 0; } +static const struct v4l2_ctrl_ops gpio_ctrl_ops = { + .s_ctrl = subdev_s_ctrl, +}; + static const struct v4l2_subdev_core_ops subdev_core_ops = { .log_status = subdev_log_status, - .g_ctrl = subdev_g_ctrl, - .s_ctrl = subdev_s_ctrl, - .queryctrl = subdev_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { @@ -375,5 +363,12 @@ int ivtv_gpio_init(struct ivtv *itv) v4l2_subdev_init(&itv->sd_gpio, &subdev_ops); snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name); itv->sd_gpio.grp_id = IVTV_HW_GPIO; + v4l2_ctrl_handler_init(&itv->hdl_gpio, 1); + v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + if (itv->hdl_gpio.error) + return itv->hdl_gpio.error; + itv->sd_gpio.ctrl_handler = &itv->hdl_gpio; + v4l2_ctrl_handler_setup(&itv->hdl_gpio); return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio); } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index a5b92d109c6..a74fa099c56 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -63,6 +63,7 @@ #include "ivtv-cards.h" #include "ivtv-gpio.h" #include "ivtv-i2c.h" +#include <media/cx25840.h> /* i2c implementation for cx23415/6 chip, ivtv project. * Author: Kevin Thayer (nufan_wfk at yahoo.com) @@ -182,8 +183,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) return -1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, type, I2C_NAME_SIZE); - return i2c_new_probed_device(adap, &info, addr_list) == NULL - ? -1 : 0; + return i2c_new_probed_device(adap, &info, addr_list, NULL) + == NULL ? -1 : 0; } /* Only allow one IR receiver to be registered per board */ @@ -220,7 +221,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) info.platform_data = init_data; strlcpy(info.type, type, I2C_NAME_SIZE); - return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; + return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? + -1 : 0; } /* Instantiate the IR receiver device using probing -- undesirable */ @@ -248,7 +250,7 @@ struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv) memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); + return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list, NULL); } int ivtv_i2c_register(struct ivtv *itv, unsigned idx) @@ -292,6 +294,12 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx])); + } else if (hw == IVTV_HW_CX25840) { + struct cx25840_platform_data pdata; + + pdata.pvr150_workaround = itv->pvr150_workaround; + sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev, + adap, mod, type, 0, &pdata, hw_addrs[idx], NULL); } else { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, hw_addrs[idx], NULL); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 11ac2fa33ef..4eed9123683 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -162,7 +162,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed) data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0; data[1] = (speed < 0); data[2] = speed < 0 ? 3 : 7; - data[3] = itv->params.video_b_frames; + data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames); data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0; data[5] = 0; data[6] = 0; @@ -339,8 +339,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f struct ivtv *itv = id->itv; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - pixfmt->width = itv->params.width; - pixfmt->height = itv->params.height; + pixfmt->width = itv->cxhdl.width; + pixfmt->height = itv->cxhdl.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; pixfmt->priv = 0; @@ -568,7 +568,6 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f { struct ivtv_open_id *id = fh; struct ivtv *itv = id->itv; - struct cx2341x_mpeg_params *p = &itv->params; struct v4l2_mbus_framefmt mbus_fmt; int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); int w = fmt->fmt.pix.width; @@ -577,15 +576,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f if (ret) return ret; - if (p->width == w && p->height == h) + if (itv->cxhdl.width == w && itv->cxhdl.height == h) return 0; if (atomic_read(&itv->capturing) > 0) return -EBUSY; - p->width = w; - p->height = h; - if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + itv->cxhdl.width = w; + itv->cxhdl.height = h; + if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) fmt->fmt.pix.width /= 2; mbus_fmt.width = fmt->fmt.pix.width; mbus_fmt.height = h; @@ -1114,9 +1113,10 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) itv->std = *std; itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; - itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; - itv->params.width = 720; - itv->params.height = itv->is_50hz ? 576 : 480; + itv->is_50hz = !itv->is_60hz; + cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz); + itv->cxhdl.width = 720; + itv->cxhdl.height = itv->is_50hz ? 576 : 480; itv->vbi.count = itv->is_50hz ? 18 : 12; itv->vbi.start[0] = itv->is_50hz ? 6 : 10; itv->vbi.start[1] = itv->is_50hz ? 318 : 273; @@ -1157,7 +1157,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); itv->main_rect.left = itv->main_rect.top = 0; itv->main_rect.width = 720; - itv->main_rect.height = itv->params.height; + itv->main_rect.height = itv->cxhdl.height; ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, 720, itv->main_rect.height, 0, 0); yi->main_rect = itv->main_rect; @@ -1554,7 +1554,7 @@ static int ivtv_log_status(struct file *file, void *fh) } IVTV_INFO("Tuner: %s\n", test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&itv->params, itv->v4l2_dev.name); + v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name); IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); for (i = 0; i < IVTV_MAX_STREAMS; i++) { struct ivtv_stream *s = &itv->streams[i]; @@ -1942,11 +1942,6 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_s_register = ivtv_s_register, #endif .vidioc_default = ivtv_default, - .vidioc_queryctrl = ivtv_queryctrl, - .vidioc_querymenu = ivtv_querymenu, - .vidioc_g_ext_ctrls = ivtv_g_ext_ctrls, - .vidioc_s_ext_ctrls = ivtv_s_ext_ctrls, - .vidioc_try_ext_ctrls = ivtv_try_ext_ctrls, .vidioc_subscribe_event = ivtv_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index fea1ec33b0d..9b4faf00919 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -71,19 +71,10 @@ static void ivtv_pio_work_handler(struct ivtv *itv) write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44); } -void ivtv_irq_work_handler(struct work_struct *work) +void ivtv_irq_work_handler(struct kthread_work *work) { - struct ivtv *itv = container_of(work, struct ivtv, irq_work_queue); + struct ivtv *itv = container_of(work, struct ivtv, irq_work); - DEFINE_WAIT(wait); - - if (test_and_clear_bit(IVTV_F_I_WORK_INITED, &itv->i_flags)) { - struct sched_param param = { .sched_priority = 99 }; - - /* This thread must use the FIFO scheduler as it - is realtime sensitive. */ - sched_setscheduler(current, SCHED_FIFO, ¶m); - } if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags)) ivtv_pio_work_handler(itv); @@ -975,7 +966,7 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) } if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) { - queue_work(itv->irq_work_queues, &itv->irq_work_queue); + queue_kthread_work(&itv->irq_worker, &itv->irq_work); } spin_unlock(&itv->dma_reg_lock); diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h index f879a5822e7..1e84433737c 100644 --- a/drivers/media/video/ivtv/ivtv-irq.h +++ b/drivers/media/video/ivtv/ivtv-irq.h @@ -46,7 +46,7 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id); -void ivtv_irq_work_handler(struct work_struct *work); +void ivtv_irq_work_handler(struct kthread_work *work); void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock); void ivtv_unfinished_dma(unsigned long arg); diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c index 84577f6f41a..e3ce9676378 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.c +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -377,3 +377,11 @@ void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, for (i = 0; i < argc; i++, p++) data[i] = readl(p); } + +/* Wipe api cache */ +void ivtv_mailbox_cache_invalidate(struct ivtv *itv) +{ + int i; + for (i = 0; i < 256; i++) + itv->api_cache[i].last_jiffies = 0; +} diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h index 8247662c928..2c834d2cb56 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.h +++ b/drivers/media/video/ivtv/ivtv-mailbox.h @@ -30,5 +30,6 @@ int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); +void ivtv_mailbox_cache_invalidate(struct ivtv *itv); #endif diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index a937e2ff9b6..512607e0cda 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -42,6 +42,7 @@ #include "ivtv-yuv.h" #include "ivtv-cards.h" #include "ivtv-streams.h" +#include "ivtv-firmware.h" #include <media/v4l2-event.h> static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { @@ -209,6 +210,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->num = num; s->vdev->v4l2_dev = &itv->v4l2_dev; + s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->fops = ivtv_stream_info[type].fops; s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; @@ -450,7 +452,6 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) { u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv *itv = s->itv; - struct cx2341x_mpeg_params *p = &itv->params; int captype = 0, subtype = 0; int enable_passthrough = 0; @@ -471,7 +472,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) } itv->mpg_data_received = itv->vbi_data_inserted = 0; itv->dualwatch_jiffies = jiffies; - itv->dualwatch_stereo_mode = p->audio_properties & 0x0300; + itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode); itv->search_pack_header = 0; break; @@ -559,12 +560,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) itv->pgm_info_offset, itv->pgm_info_num); /* Setup API for Stream */ - cx2341x_update(itv, ivtv_api_func, NULL, p); + cx2341x_handler_setup(&itv->cxhdl); /* mute if capturing radio */ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, - 1 | (p->video_mute_yuv << 8)); + 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); } /* Vsync Setup */ @@ -580,6 +581,8 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) clear_bit(IVTV_F_I_EOS, &itv->i_flags); + cx2341x_handler_set_busy(&itv->cxhdl, 1); + /* Initialize Digitizer for Capture */ /* Avoid tinny audio problem - ensure audio clocks are going */ v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); @@ -616,7 +619,6 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) { u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv *itv = s->itv; - struct cx2341x_mpeg_params *p = &itv->params; int datatype; u16 width; u16 height; @@ -626,8 +628,8 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); - width = p->width; - height = p->height; + width = itv->cxhdl.width; + height = itv->cxhdl.height; /* set audio mode to left/stereo for dual/stereo mode. */ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); @@ -667,19 +669,21 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) break; } if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, - width, height, p->audio_properties)) { + width, height, itv->cxhdl.audio_properties)) { IVTV_DEBUG_WARN("Couldn't initialize decoder source\n"); } /* Decoder sometimes dies here, so wait a moment */ ivtv_msleep_timeout(10, 0); - return 0; + /* Known failure point for firmware, so check */ + return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream"); } int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) { struct ivtv *itv = s->itv; + int rc; if (s->vdev == NULL) return -EINVAL; @@ -689,7 +693,11 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); - ivtv_setup_v4l2_decode_stream(s); + rc = ivtv_setup_v4l2_decode_stream(s); + if (rc < 0) { + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + return rc; + } /* set dma size to 65536 bytes */ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); @@ -840,6 +848,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) return 0; } + cx2341x_handler_set_busy(&itv->cxhdl, 0); + /* Set the following Interrupt mask bits for capture */ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); del_timer(&itv->dma_timer); @@ -960,7 +970,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable) /* Setup capture if not already done */ if (atomic_read(&itv->capturing) == 0) { - cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + cx2341x_handler_setup(&itv->cxhdl); + cx2341x_handler_set_busy(&itv->cxhdl, 1); } /* Start Passthrough Mode */ @@ -981,6 +992,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable) clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); itv->output_mode = OUT_NONE; + if (atomic_read(&itv->capturing) == 0) + cx2341x_handler_set_busy(&itv->cxhdl, 0); return 0; } diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h index b530dec399d..b67a4048f5a 100644 --- a/drivers/media/video/ivtv/ivtv-version.h +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -23,7 +23,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 #define IVTV_DRIVER_VERSION_MINOR 4 -#define IVTV_DRIVER_VERSION_PATCHLEVEL 1 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 2 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index 9ff3425891e..be03a712731 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -53,6 +53,7 @@ #include "ivtv-i2c.h" #include "ivtv-udma.h" #include "ivtv-mailbox.h" +#include "ivtv-firmware.h" /* card parameters */ static int ivtvfb_card_id = -1; @@ -178,6 +179,12 @@ struct osd_info { struct fb_info ivtvfb_info; struct fb_var_screeninfo ivtvfb_defined; struct fb_fix_screeninfo ivtvfb_fix; + + /* Used for a warm start */ + struct fb_var_screeninfo fbvar_cur; + int blank_cur; + u32 palette_cur[256]; + u32 pan_cur; }; struct ivtv_osd_coords { @@ -199,6 +206,7 @@ static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase, u32 data[CX2341X_MBOX_MAX_DATA]; int rc; + ivtv_firmware_check(itv, "ivtvfb_get_framebuffer"); rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); *fbbase = data[0]; *fblength = data[1]; @@ -581,8 +589,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) ivtv_window.height = var->yres; /* Minimum margin cannot be 0, as X won't allow such a mode */ - if (!var->upper_margin) var->upper_margin++; - if (!var->left_margin) var->left_margin++; + if (!var->upper_margin) + var->upper_margin++; + if (!var->left_margin) + var->left_margin++; ivtv_window.top = var->upper_margin - 1; ivtv_window.left = var->left_margin - 1; @@ -595,6 +605,9 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) /* Force update of yuv registers */ itv->yuv_info.yuv_forced_update = 1; + /* Keep a copy of these settings */ + memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur)); + IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", var->xres, var->yres, var->xres_virtual, var->yres_virtual, @@ -829,6 +842,8 @@ static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *inf itv->yuv_info.osd_y_pan = var->yoffset; /* Force update of yuv registers */ itv->yuv_info.yuv_forced_update = 1; + /* Remember this value */ + itv->osd_info->pan_cur = osd_pan_index; return 0; } @@ -842,6 +857,7 @@ static int ivtvfb_set_par(struct fb_info *info) rc = ivtvfb_set_var(itv, &info->var); ivtvfb_pan_display(&info->var, info); ivtvfb_get_fix(itv, &info->fix); + ivtv_firmware_check(itv, "ivtvfb_set_par"); return rc; } @@ -859,6 +875,7 @@ static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, if (info->var.bits_per_pixel <= 8) { write_reg(regno, 0x02a30); write_reg(color, 0x02a34); + itv->osd_info->palette_cur[regno] = color; return 0; } if (regno >= 16) @@ -911,6 +928,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); break; } + itv->osd_info->blank_cur = blank_mode; return 0; } @@ -929,6 +947,21 @@ static struct fb_ops ivtvfb_ops = { .fb_blank = ivtvfb_blank, }; +/* Restore hardware after firmware restart */ +static void ivtvfb_restore(struct ivtv *itv) +{ + struct osd_info *oi = itv->osd_info; + int i; + + ivtvfb_set_var(itv, &oi->fbvar_cur); + ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info); + for (i = 0; i < 256; i++) { + write_reg(i, 0x02a30); + write_reg(oi->palette_cur[i], 0x02a34); + } + write_reg(oi->pan_cur, 0x02a0c); +} + /* Initialization */ @@ -1192,6 +1225,9 @@ static int ivtvfb_init_card(struct ivtv *itv) /* Enable the osd */ ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); + /* Enable restart */ + itv->ivtvfb_restore = ivtvfb_restore; + /* Allocate DMA */ ivtv_udma_alloc(itv); return 0; @@ -1203,7 +1239,7 @@ static int __init ivtvfb_callback_init(struct device *dev, void *p) struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { if (ivtvfb_init_card(itv) == 0) { IVTVFB_INFO("Framebuffer registered on %s\n", itv->v4l2_dev.name); @@ -1219,13 +1255,14 @@ static int ivtvfb_callback_cleanup(struct device *dev, void *p) struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); struct osd_info *oi = itv->osd_info; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", itv->instance); return 0; } IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); + itv->ivtvfb_restore = NULL; ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); ivtvfb_release_buffers(itv); itv->osd_video_pbase = 0; diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 10ddeccc70e..4525335f9bd 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -903,14 +903,14 @@ static int m2mtest_release(struct file *file) static unsigned int m2mtest_poll(struct file *file, struct poll_table_struct *wait) { - struct m2mtest_ctx *ctx = (struct m2mtest_ctx *)file->private_data; + struct m2mtest_ctx *ctx = file->private_data; return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) { - struct m2mtest_ctx *ctx = (struct m2mtest_ctx *)file->private_data; + struct m2mtest_ctx *ctx = file->private_data; return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index e9df3cb02cc..0e412131da7 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out) msp_write_dem(client, 0x40, state->i2s_mode); } -void msp_set_audio(struct i2c_client *client) -{ - struct msp_state *state = to_state(i2c_get_clientdata(client)); - int bal = 0, bass, treble, loudness; - int val = 0; - int reallymuted = state->muted | state->scan_in_progress; - - if (!reallymuted) - val = (state->volume * 0x7f / 65535) << 8; - - v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", - state->muted ? "on" : "off", - state->scan_in_progress ? "yes" : "no", - state->volume); - - msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); - if (state->has_headphones) - msp_write_dsp(client, 0x0006, val); - if (!state->has_sound_processing) - return; - - if (val) - bal = (u8)((state->balance / 256) - 128); - bass = ((state->bass - 32768) * 0x60 / 65535) << 8; - treble = ((state->treble - 32768) * 0x60 / 65535) << 8; - loudness = state->loudness ? ((5 * 4) << 8) : 0; - - v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n", - state->balance, state->bass, state->treble, state->loudness); - - msp_write_dsp(client, 0x0001, bal << 8); - msp_write_dsp(client, 0x0002, bass); - msp_write_dsp(client, 0x0003, treble); - msp_write_dsp(client, 0x0004, loudness); - if (!state->has_headphones) - return; - msp_write_dsp(client, 0x0030, bal << 8); - msp_write_dsp(client, 0x0031, bass); - msp_write_dsp(client, 0x0032, treble); - msp_write_dsp(client, 0x0033, loudness); -} - /* ------------------------------------------------------------------------ */ static void msp_wake_thread(struct i2c_client *client) @@ -363,98 +318,73 @@ int msp_sleep(struct msp_state *state, int timeout) /* ------------------------------------------------------------------------ */ -static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int msp_s_ctrl(struct v4l2_ctrl *ctrl) { - struct msp_state *state = to_state(sd); + struct msp_state *state = ctrl_to_state(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + int val = ctrl->val; switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = state->volume; - break; - - case V4L2_CID_AUDIO_MUTE: - ctrl->value = state->muted; - break; - - case V4L2_CID_AUDIO_BALANCE: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->balance; - break; - - case V4L2_CID_AUDIO_BASS: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->bass; + case V4L2_CID_AUDIO_VOLUME: { + /* audio volume cluster */ + int reallymuted = state->muted->val | state->scan_in_progress; + + if (!reallymuted) + val = (val * 0x7f / 65535) << 8; + + v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", + state->muted->val ? "on" : "off", + state->scan_in_progress ? "yes" : "no", + state->volume->val); + + msp_write_dsp(client, 0x0000, val); + msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, val); break; - - case V4L2_CID_AUDIO_TREBLE: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->treble; - break; - - case V4L2_CID_AUDIO_LOUDNESS: - if (!state->has_sound_processing) - return -EINVAL; - ctrl->value = state->loudness; - break; - - default: - return -EINVAL; } - return 0; -} - -static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - state->volume = ctrl->value; - if (state->volume == 0) - state->balance = 32768; - break; - - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value < 0 || ctrl->value >= 2) - return -ERANGE; - state->muted = ctrl->value; - break; case V4L2_CID_AUDIO_BASS: - if (!state->has_sound_processing) - return -EINVAL; - state->bass = ctrl->value; + val = ((val - 32768) * 0x60 / 65535) << 8; + msp_write_dsp(client, 0x0002, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0031, val); break; case V4L2_CID_AUDIO_TREBLE: - if (!state->has_sound_processing) - return -EINVAL; - state->treble = ctrl->value; + val = ((val - 32768) * 0x60 / 65535) << 8; + msp_write_dsp(client, 0x0003, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0032, val); break; case V4L2_CID_AUDIO_LOUDNESS: - if (!state->has_sound_processing) - return -EINVAL; - state->loudness = ctrl->value; + val = val ? ((5 * 4) << 8) : 0; + msp_write_dsp(client, 0x0004, val); + if (state->has_headphones) + msp_write_dsp(client, 0x0033, val); break; case V4L2_CID_AUDIO_BALANCE: - if (!state->has_sound_processing) - return -EINVAL; - state->balance = ctrl->value; + val = (u8)((val / 256) - 128); + msp_write_dsp(client, 0x0001, val << 8); + if (state->has_headphones) + msp_write_dsp(client, 0x0030, val << 8); break; default: return -EINVAL; } - msp_set_audio(client); return 0; } +void msp_update_volume(struct msp_state *state) +{ + v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume)); +} + /* --- v4l2 ioctls --- */ static int msp_s_radio(struct v4l2_subdev *sd) { @@ -472,7 +402,7 @@ static int msp_s_radio(struct v4l2_subdev *sd) msp3400c_set_mode(client, MSP_MODE_FM_RADIO); msp3400c_set_carrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); - msp_set_audio(client); + msp_update_volume(state); break; case OPMODE_AUTODETECT: case OPMODE_AUTOSELECT: @@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) return 0; } -static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct msp_state *state = to_state(sd); - - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - default: - break; - } - if (!state->has_sound_processing) - return -EINVAL; - switch (qc->id) { - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - default: - return -EINVAL; - } - return 0; -} - static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct msp_state *state = to_state(sd); @@ -633,19 +536,14 @@ static int msp_log_status(struct v4l2_subdev *sd) struct msp_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); const char *p; + char prefix[V4L2_SUBDEV_NAME_SIZE + 20]; if (state->opmode == OPMODE_AUTOSELECT) msp_detect_stereo(client); v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", client->name, state->rev1, state->rev2); - v4l_info(client, "Audio: volume %d%s\n", - state->volume, state->muted ? " (muted)" : ""); - if (state->has_sound_processing) { - v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", - state->balance, state->bass, - state->treble, - state->loudness ? "on" : "off"); - } + snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name); + v4l2_ctrl_handler_log_status(&state->hdl, prefix); switch (state->mode) { case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; case MSP_MODE_FM_RADIO: p = "FM Radio"; break; @@ -695,12 +593,20 @@ static int msp_resume(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops msp_ctrl_ops = { + .s_ctrl = msp_s_ctrl, +}; + static const struct v4l2_subdev_core_ops msp_core_ops = { .log_status = msp_log_status, .g_chip_ident = msp_g_chip_ident, - .g_ctrl = msp_g_ctrl, - .s_ctrl = msp_s_ctrl, - .queryctrl = msp_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = msp_s_std, }; @@ -728,6 +634,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct msp_state *state; struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; int (*thread_func)(void *data) = NULL; int msp_hard; int msp_family; @@ -752,13 +659,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) state->v4l2_std = V4L2_STD_NTSC; state->audmode = V4L2_TUNER_MODE_STEREO; - state->volume = 58880; /* 0db gain */ - state->balance = 32768; /* 0db gain */ - state->bass = 32768; - state->treble = 32768; - state->loudness = 0; state->input = -1; - state->muted = 0; state->i2s_mode = 0; init_waitqueue_head(&state->wq); /* These are the reset input/output positions */ @@ -777,8 +678,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENODEV; } - msp_set_audio(client); - msp_family = ((state->rev1 >> 4) & 0x0f) + 3; msp_product = (state->rev2 >> 8) & 0xff; msp_prod_hi = msp_product / 10; @@ -849,6 +748,34 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) state->opmode = OPMODE_MANUAL; } + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + if (state->has_sound_processing) { + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0); + } + state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880); + v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + + v4l2_ctrl_cluster(2, &state->volume); + v4l2_ctrl_handler_setup(hdl); + /* hello world :-) */ v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", msp_family, msp_product, @@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client) } msp_reset(client); + v4l2_ctrl_handler_free(&state->hdl); kfree(state); return 0; } diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index d6b3e6d0eef..32a478e532f 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -6,6 +6,7 @@ #include <media/msp3400.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> /* ---------------------------------------------------------------------- */ @@ -51,6 +52,7 @@ extern int msp_stereo_thresh; struct msp_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; int rev1, rev2; int ident; u8 has_nicam; @@ -87,9 +89,12 @@ struct msp_state { int audmode; int rxsubchans; - int volume, muted; - int balance, loudness; - int bass, treble; + struct { + /* volume cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *muted; + }; + int scan_in_progress; /* thread */ @@ -104,6 +109,11 @@ static inline struct msp_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct msp_state, sd); } +static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct msp_state, hdl); +} + /* msp3400-driver.c */ int msp_write_dem(struct i2c_client *client, int addr, int val); int msp_write_dsp(struct i2c_client *client, int addr, int val); @@ -111,7 +121,7 @@ int msp_read_dem(struct i2c_client *client, int addr); int msp_read_dsp(struct i2c_client *client, int addr); int msp_reset(struct i2c_client *client); void msp_set_scart(struct i2c_client *client, int in, int out); -void msp_set_audio(struct i2c_client *client); +void msp_update_volume(struct msp_state *state); int msp_sleep(struct msp_state *state, int timeout); /* msp3400-kthreads.c */ diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index d5a69c5ee5e..b376fcdee65 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -496,13 +496,13 @@ restart: v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); state->scan_in_progress = 0; - msp_set_audio(client); + msp_update_volume(state); continue; } /* mute audio */ state->scan_in_progress = 1; - msp_set_audio(client); + msp_update_volume(state); msp3400c_set_mode(client, MSP_MODE_AM_DETECT); val1 = val2 = 0; @@ -634,7 +634,7 @@ no_second: /* unmute */ state->scan_in_progress = 0; msp3400c_set_audmode(client); - msp_set_audio(client); + msp_update_volume(state); if (msp_debug) msp3400c_print_mode(client); @@ -679,13 +679,13 @@ restart: v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); state->scan_in_progress = 0; - msp_set_audio(client); + msp_update_volume(state); continue; } /* mute audio */ state->scan_in_progress = 1; - msp_set_audio(client); + msp_update_volume(state); /* start autodetect. Note: autodetect is not supported for NTSC-M and radio, hence we force the standard in those @@ -797,7 +797,7 @@ restart: /* unmute */ msp3400c_set_audmode(client); state->scan_in_progress = 0; - msp_set_audio(client); + msp_update_volume(state); /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ @@ -974,7 +974,7 @@ restart: v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); state->scan_in_progress = 0; - msp_set_audio(client); + msp_update_volume(state); continue; } @@ -1020,7 +1020,7 @@ unmute: } /* unmute: dispatch sound to scart output, set scart volume */ - msp_set_audio(client); + msp_update_volume(state); /* restore ACB */ if (msp_write_dsp(client, 0x13, state->acb)) diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index fbd0fc79472..758a4db27d6 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -1,5 +1,5 @@ /* - * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron + * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina * * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> * @@ -19,11 +19,14 @@ #include <media/soc_camera.h> /* - * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin) + * MT9M111, MT9M112 and MT9M131: + * i2c address is 0x48 or 0x5d (depending on SADDR pin) * The platform has to define i2c_board_info and call i2c_register_board_info() */ -/* mt9m111: Sensor register addresses */ +/* + * Sensor core register addresses (0x000..0x0ff) + */ #define MT9M111_CHIP_VERSION 0x000 #define MT9M111_ROW_START 0x001 #define MT9M111_COLUMN_START 0x002 @@ -72,8 +75,9 @@ #define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2) #define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1) #define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0) + /* - * mt9m111: Colorpipe register addresses (0x100..0x1ff) + * Colorpipe register addresses (0x100..0x1ff) */ #define MT9M111_OPER_MODE_CTRL 0x106 #define MT9M111_OUTPUT_FORMAT_CTRL 0x108 @@ -109,8 +113,9 @@ #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1) #define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1) #define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0) + /* - * mt9m111: Camera control register addresses (0x200..0x2ff not implemented) + * Camera control register addresses (0x200..0x2ff not implemented) */ #define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg) @@ -143,10 +148,10 @@ static const struct mt9m111_datafmt *mt9m111_find_datafmt( } static const struct mt9m111_datafmt mt9m111_colour_fmts[] = { - {V4L2_MBUS_FMT_YUYV8_2X8_LE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YVYU8_2X8_LE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YUYV8_2X8_BE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YVYU8_2X8_BE, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG}, {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, @@ -160,7 +165,8 @@ enum mt9m111_context { struct mt9m111 { struct v4l2_subdev subdev; - int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */ + int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code + * from v4l2-chip-ident.h */ enum mt9m111_context context; struct v4l2_rect rect; const struct mt9m111_datafmt *fmt; @@ -505,22 +511,22 @@ static int mt9m111_set_pixfmt(struct i2c_client *client, case V4L2_MBUS_FMT_RGB565_2X8_LE: ret = mt9m111_setfmt_rgb565(client); break; - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 0; ret = mt9m111_setfmt_yuv(client); break; - case V4L2_MBUS_FMT_YVYU8_2X8_BE: + case V4L2_MBUS_FMT_VYUY8_2X8: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 1; ret = mt9m111_setfmt_yuv(client); break; - case V4L2_MBUS_FMT_YUYV8_2X8_LE: + case V4L2_MBUS_FMT_YUYV8_2X8: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 0; ret = mt9m111_setfmt_yuv(client); break; - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_YVYU8_2X8: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 1; ret = mt9m111_setfmt_yuv(client); @@ -934,7 +940,7 @@ static int mt9m111_init(struct i2c_client *client) if (!ret) ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure); if (ret) - dev_err(&client->dev, "mt9m11x init failed: %d\n", ret); + dev_err(&client->dev, "mt9m111 init failed: %d\n", ret); return ret; } @@ -963,27 +969,27 @@ static int mt9m111_video_probe(struct soc_camera_device *icd, mt9m111->swap_rgb_even_odd = 1; mt9m111->swap_rgb_red_blue = 1; - ret = mt9m111_init(client); - if (ret) - goto ei2c; - data = reg_read(CHIP_VERSION); switch (data) { - case 0x143a: /* MT9M111 */ + case 0x143a: /* MT9M111 or MT9M131 */ mt9m111->model = V4L2_IDENT_MT9M111; + dev_info(&client->dev, + "Detected a MT9M111/MT9M131 chip ID %x\n", data); break; case 0x148c: /* MT9M112 */ mt9m111->model = V4L2_IDENT_MT9M112; + dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data); break; default: ret = -ENODEV; dev_err(&client->dev, - "No MT9M11x chip detected, register read %x\n", data); + "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n", + data); goto ei2c; } - dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data); + ret = mt9m111_init(client); ei2c: return ret; @@ -1034,13 +1040,13 @@ static int mt9m111_probe(struct i2c_client *client, int ret; if (!icd) { - dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n"); + dev_err(&client->dev, "mt9m111: soc-camera data missing!\n"); return -EINVAL; } icl = to_soc_camera_link(icd); if (!icl) { - dev_err(&client->dev, "MT9M11x driver needs platform data\n"); + dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; } @@ -1114,6 +1120,6 @@ static void __exit mt9m111_mod_exit(void) module_init(mt9m111_mod_init); module_exit(mt9m111_mod_exit); -MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver"); +MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver"); MODULE_AUTHOR("Robert Jarzmik"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index e4bf1db9a87..8ec47e42d4d 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -121,22 +121,22 @@ struct mt9t112_priv { static const struct mt9t112_format mt9t112_cfmts[] = { { - .code = V4L2_MBUS_FMT_YUYV8_2X8_BE, + .code = V4L2_MBUS_FMT_UYVY8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 0, }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8_BE, + .code = V4L2_MBUS_FMT_VYUY8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 1, }, { - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 2, }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8_LE, + .code = V4L2_MBUS_FMT_YVYU8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 3, @@ -972,7 +972,7 @@ static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) struct v4l2_rect *rect = &a->c; return mt9t112_set_params(client, rect->width, rect->height, - V4L2_MBUS_FMT_YUYV8_2X8_BE); + V4L2_MBUS_FMT_UYVY8_2X8); } static int mt9t112_g_fmt(struct v4l2_subdev *sd, @@ -983,7 +983,7 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd, if (!priv->format) { int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT, - V4L2_MBUS_FMT_YUYV8_2X8_BE); + V4L2_MBUS_FMT_UYVY8_2X8); if (ret < 0) return ret; } diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c new file mode 100644 index 00000000000..66ff174151b --- /dev/null +++ b/drivers/media/video/mx2_camera.c @@ -0,0 +1,1515 @@ +/* + * V4L2 Driver for i.MX27/i.MX25 camera host + * + * Copyright (C) 2008, Sascha Hauer, Pengutronix + * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> +#include <linux/time.h> +#include <linux/version.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/clk.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/videobuf-dma-contig.h> +#include <media/soc_camera.h> +#include <media/soc_mediabus.h> + +#include <linux/videodev2.h> + +#include <mach/mx2_cam.h> +#ifdef CONFIG_MACH_MX27 +#include <mach/dma-mx1-mx2.h> +#endif +#include <mach/hardware.h> + +#include <asm/dma.h> + +#define MX2_CAM_DRV_NAME "mx2-camera" +#define MX2_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) +#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera" + +/* reset values */ +#define CSICR1_RESET_VAL 0x40000800 +#define CSICR2_RESET_VAL 0x0 +#define CSICR3_RESET_VAL 0x0 + +/* csi control reg 1 */ +#define CSICR1_SWAP16_EN (1 << 31) +#define CSICR1_EXT_VSYNC (1 << 30) +#define CSICR1_EOF_INTEN (1 << 29) +#define CSICR1_PRP_IF_EN (1 << 28) +#define CSICR1_CCIR_MODE (1 << 27) +#define CSICR1_COF_INTEN (1 << 26) +#define CSICR1_SF_OR_INTEN (1 << 25) +#define CSICR1_RF_OR_INTEN (1 << 24) +#define CSICR1_STATFF_LEVEL (3 << 22) +#define CSICR1_STATFF_INTEN (1 << 21) +#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */ +#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */ +#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */ +#define CSICR1_RXFF_INTEN (1 << 18) +#define CSICR1_SOF_POL (1 << 17) +#define CSICR1_SOF_INTEN (1 << 16) +#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12) +#define CSICR1_HSYNC_POL (1 << 11) +#define CSICR1_CCIR_EN (1 << 10) +#define CSICR1_MCLKEN (1 << 9) +#define CSICR1_FCC (1 << 8) +#define CSICR1_PACK_DIR (1 << 7) +#define CSICR1_CLR_STATFIFO (1 << 6) +#define CSICR1_CLR_RXFIFO (1 << 5) +#define CSICR1_GCLK_MODE (1 << 4) +#define CSICR1_INV_DATA (1 << 3) +#define CSICR1_INV_PCLK (1 << 2) +#define CSICR1_REDGE (1 << 1) + +#define SHIFT_STATFF_LEVEL 22 +#define SHIFT_RXFF_LEVEL 19 +#define SHIFT_MCLKDIV 12 + +/* control reg 3 */ +#define CSICR3_FRMCNT (0xFFFF << 16) +#define CSICR3_FRMCNT_RST (1 << 15) +#define CSICR3_DMA_REFLASH_RFF (1 << 14) +#define CSICR3_DMA_REFLASH_SFF (1 << 13) +#define CSICR3_DMA_REQ_EN_RFF (1 << 12) +#define CSICR3_DMA_REQ_EN_SFF (1 << 11) +#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */ +#define CSICR3_CSI_SUP (1 << 3) +#define CSICR3_ZERO_PACK_EN (1 << 2) +#define CSICR3_ECC_INT_EN (1 << 1) +#define CSICR3_ECC_AUTO_EN (1 << 0) + +#define SHIFT_FRMCNT 16 + +/* csi status reg */ +#define CSISR_SFF_OR_INT (1 << 25) +#define CSISR_RFF_OR_INT (1 << 24) +#define CSISR_STATFF_INT (1 << 21) +#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */ +#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */ +#define CSISR_RXFF_INT (1 << 18) +#define CSISR_EOF_INT (1 << 17) +#define CSISR_SOF_INT (1 << 16) +#define CSISR_F2_INT (1 << 15) +#define CSISR_F1_INT (1 << 14) +#define CSISR_COF_INT (1 << 13) +#define CSISR_ECC_INT (1 << 1) +#define CSISR_DRDY (1 << 0) + +#define CSICR1 0x00 +#define CSICR2 0x04 +#define CSISR (cpu_is_mx27() ? 0x08 : 0x18) +#define CSISTATFIFO 0x0c +#define CSIRFIFO 0x10 +#define CSIRXCNT 0x14 +#define CSICR3 (cpu_is_mx27() ? 0x1C : 0x08) +#define CSIDMASA_STATFIFO 0x20 +#define CSIDMATA_STATFIFO 0x24 +#define CSIDMASA_FB1 0x28 +#define CSIDMASA_FB2 0x2c +#define CSIFBUF_PARA 0x30 +#define CSIIMAG_PARA 0x34 + +/* EMMA PrP */ +#define PRP_CNTL 0x00 +#define PRP_INTR_CNTL 0x04 +#define PRP_INTRSTATUS 0x08 +#define PRP_SOURCE_Y_PTR 0x0c +#define PRP_SOURCE_CB_PTR 0x10 +#define PRP_SOURCE_CR_PTR 0x14 +#define PRP_DEST_RGB1_PTR 0x18 +#define PRP_DEST_RGB2_PTR 0x1c +#define PRP_DEST_Y_PTR 0x20 +#define PRP_DEST_CB_PTR 0x24 +#define PRP_DEST_CR_PTR 0x28 +#define PRP_SRC_FRAME_SIZE 0x2c +#define PRP_DEST_CH1_LINE_STRIDE 0x30 +#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 +#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 +#define PRP_CH1_OUT_IMAGE_SIZE 0x3c +#define PRP_CH2_OUT_IMAGE_SIZE 0x40 +#define PRP_SRC_LINE_STRIDE 0x44 +#define PRP_CSC_COEF_012 0x48 +#define PRP_CSC_COEF_345 0x4c +#define PRP_CSC_COEF_678 0x50 +#define PRP_CH1_RZ_HORI_COEF1 0x54 +#define PRP_CH1_RZ_HORI_COEF2 0x58 +#define PRP_CH1_RZ_HORI_VALID 0x5c +#define PRP_CH1_RZ_VERT_COEF1 0x60 +#define PRP_CH1_RZ_VERT_COEF2 0x64 +#define PRP_CH1_RZ_VERT_VALID 0x68 +#define PRP_CH2_RZ_HORI_COEF1 0x6c +#define PRP_CH2_RZ_HORI_COEF2 0x70 +#define PRP_CH2_RZ_HORI_VALID 0x74 +#define PRP_CH2_RZ_VERT_COEF1 0x78 +#define PRP_CH2_RZ_VERT_COEF2 0x7c +#define PRP_CH2_RZ_VERT_VALID 0x80 + +#define PRP_CNTL_CH1EN (1 << 0) +#define PRP_CNTL_CH2EN (1 << 1) +#define PRP_CNTL_CSIEN (1 << 2) +#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) +#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) +#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) +#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) +#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) +#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) +#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) +#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) +#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) +#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) +#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) +#define PRP_CNTL_CH1_LEN (1 << 9) +#define PRP_CNTL_CH2_LEN (1 << 10) +#define PRP_CNTL_SKIP_FRAME (1 << 11) +#define PRP_CNTL_SWRST (1 << 12) +#define PRP_CNTL_CLKEN (1 << 13) +#define PRP_CNTL_WEN (1 << 14) +#define PRP_CNTL_CH1BYP (1 << 15) +#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) +#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) +#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) +#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) +#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) +#define PRP_CNTL_CH2B1EN (1 << 29) +#define PRP_CNTL_CH2B2EN (1 << 30) +#define PRP_CNTL_CH2FEN (1 << 31) + +/* IRQ Enable and status register */ +#define PRP_INTR_RDERR (1 << 0) +#define PRP_INTR_CH1WERR (1 << 1) +#define PRP_INTR_CH2WERR (1 << 2) +#define PRP_INTR_CH1FC (1 << 3) +#define PRP_INTR_CH2FC (1 << 5) +#define PRP_INTR_LBOVF (1 << 7) +#define PRP_INTR_CH2OVF (1 << 8) + +#define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma) + +#define MAX_VIDEO_MEM 16 + +struct mx2_camera_dev { + struct device *dev; + struct soc_camera_host soc_host; + struct soc_camera_device *icd; + struct clk *clk_csi, *clk_emma; + + unsigned int irq_csi, irq_emma; + void __iomem *base_csi, *base_emma; + unsigned long base_dma; + + struct mx2_camera_platform_data *pdata; + struct resource *res_csi, *res_emma; + unsigned long platform_flags; + + struct list_head capture; + struct list_head active_bufs; + + spinlock_t lock; + + int dma; + struct mx2_buffer *active; + struct mx2_buffer *fb1_active; + struct mx2_buffer *fb2_active; + + int use_emma; + + u32 csicr1; + + void *discard_buffer; + dma_addr_t discard_buffer_dma; + size_t discard_size; +}; + +/* buffer for one video frame */ +struct mx2_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + enum v4l2_mbus_pixelcode code; + + int bufnum; +}; + +static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) +{ + unsigned long flags; + + clk_disable(pcdev->clk_csi); + writel(0, pcdev->base_csi + CSICR1); + if (mx27_camera_emma(pcdev)) { + writel(0, pcdev->base_emma + PRP_CNTL); + } else if (cpu_is_mx25()) { + spin_lock_irqsave(&pcdev->lock, flags); + pcdev->fb1_active = NULL; + pcdev->fb2_active = NULL; + writel(0, pcdev->base_csi + CSIDMASA_FB1); + writel(0, pcdev->base_csi + CSIDMASA_FB2); + spin_unlock_irqrestore(&pcdev->lock, flags); + } +} + +/* + * The following two functions absolutely depend on the fact, that + * there can be only one camera on mx2 camera sensor interface + */ +static int mx2_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + int ret; + u32 csicr1; + + if (pcdev->icd) + return -EBUSY; + + ret = clk_enable(pcdev->clk_csi); + if (ret < 0) + return ret; + + csicr1 = CSICR1_MCLKEN; + + if (mx27_camera_emma(pcdev)) { + csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | + CSICR1_RXFF_LEVEL(0); + } else if (cpu_is_mx27()) + csicr1 |= CSICR1_SOF_INTEN | CSICR1_RXFF_LEVEL(2); + + pcdev->csicr1 = csicr1; + writel(pcdev->csicr1, pcdev->base_csi + CSICR1); + + pcdev->icd = icd; + + dev_info(icd->dev.parent, "Camera driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void mx2_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + + BUG_ON(icd != pcdev->icd); + + dev_info(icd->dev.parent, "Camera driver detached from camera %d\n", + icd->devnum); + + mx2_camera_deactivate(pcdev); + + if (pcdev->discard_buffer) { + dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, + pcdev->discard_buffer, + pcdev->discard_buffer_dma); + pcdev->discard_buffer = NULL; + } + + pcdev->icd = NULL; +} + +#ifdef CONFIG_MACH_MX27 +static void mx27_camera_dma_enable(struct mx2_camera_dev *pcdev) +{ + u32 tmp; + + imx_dma_enable(pcdev->dma); + + tmp = readl(pcdev->base_csi + CSICR1); + tmp |= CSICR1_RF_OR_INTEN; + writel(tmp, pcdev->base_csi + CSICR1); +} + +static irqreturn_t mx27_camera_irq(int irq_csi, void *data) +{ + struct mx2_camera_dev *pcdev = data; + u32 status = readl(pcdev->base_csi + CSISR); + + if (status & CSISR_SOF_INT && pcdev->active) { + u32 tmp; + + tmp = readl(pcdev->base_csi + CSICR1); + writel(tmp | CSICR1_CLR_RXFIFO, pcdev->base_csi + CSICR1); + mx27_camera_dma_enable(pcdev); + } + + writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR); + + return IRQ_HANDLED; +} +#else +static irqreturn_t mx27_camera_irq(int irq_csi, void *data) +{ + return IRQ_NONE; +} +#endif /* CONFIG_MACH_MX27 */ + +static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, + int state) +{ + struct videobuf_buffer *vb; + struct mx2_buffer *buf; + struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : + &pcdev->fb2_active; + u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + vb = &(*fb_active)->vb; + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + vb->state = state; + do_gettimeofday(&vb->ts); + vb->field_count++; + + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + buf = NULL; + writel(0, pcdev->base_csi + fb_reg); + } else { + buf = list_entry(pcdev->capture.next, struct mx2_buffer, + vb.queue); + vb = &buf->vb; + list_del(&vb->queue); + vb->state = VIDEOBUF_ACTIVE; + writel(videobuf_to_dma_contig(vb), pcdev->base_csi + fb_reg); + } + + *fb_active = buf; + + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static irqreturn_t mx25_camera_irq(int irq_csi, void *data) +{ + struct mx2_camera_dev *pcdev = data; + u32 status = readl(pcdev->base_csi + CSISR); + + if (status & CSISR_DMA_TSF_FB1_INT) + mx25_camera_frame_done(pcdev, 1, VIDEOBUF_DONE); + else if (status & CSISR_DMA_TSF_FB2_INT) + mx25_camera_frame_done(pcdev, 2, VIDEOBUF_DONE); + + /* FIXME: handle CSISR_RFF_OR_INT */ + + writel(status, pcdev->base_csi + CSISR); + + return IRQ_HANDLED; +} + +/* + * Videobuf operations + */ +static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + + if (bytes_per_line < 0) + return bytes_per_line; + + *size = bytes_per_line * icd->user_height; + + if (0 == *count) + *count = 32; + if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + struct videobuf_buffer *vb = &buf->vb; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* + * This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE + */ + videobuf_waiton(vb, 0, 0); + + videobuf_dma_contig_free(vq, vb); + dev_dbg(&icd->dev, "%s freed\n", __func__); + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int mx2_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + int ret = 0; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + if (bytes_per_line < 0) + return bytes_per_line; + +#ifdef DEBUG + /* + * This can be useful if you want to see if we actually fill + * the buffer with something + */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + if (buf->code != icd->current_fmt->code || + vb->width != icd->user_width || + vb->height != icd->user_height || + vb->field != field) { + buf->code = icd->current_fmt->code; + vb->width = icd->user_width; + vb->height = icd->user_height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + vb->size = bytes_per_line * vb->height; + if (vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + vb->state = VIDEOBUF_PREPARED; + } + + return 0; + +fail: + free_buffer(vq, buf); +out: + return ret; +} + +static void mx2_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + unsigned long flags; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + spin_lock_irqsave(&pcdev->lock, flags); + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &pcdev->capture); + + if (mx27_camera_emma(pcdev)) { + goto out; +#ifdef CONFIG_MACH_MX27 + } else if (cpu_is_mx27()) { + int ret; + + if (pcdev->active == NULL) { + ret = imx_dma_setup_single(pcdev->dma, + videobuf_to_dma_contig(vb), vb->size, + (u32)pcdev->base_dma + 0x10, + DMA_MODE_READ); + if (ret) { + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + goto out; + } + + vb->state = VIDEOBUF_ACTIVE; + pcdev->active = buf; + } +#endif + } else { /* cpu_is_mx25() */ + u32 csicr3, dma_inten = 0; + + if (pcdev->fb1_active == NULL) { + writel(videobuf_to_dma_contig(vb), + pcdev->base_csi + CSIDMASA_FB1); + pcdev->fb1_active = buf; + dma_inten = CSICR1_FB1_DMA_INTEN; + } else if (pcdev->fb2_active == NULL) { + writel(videobuf_to_dma_contig(vb), + pcdev->base_csi + CSIDMASA_FB2); + pcdev->fb2_active = buf; + dma_inten = CSICR1_FB2_DMA_INTEN; + } + + if (dma_inten) { + list_del(&vb->queue); + vb->state = VIDEOBUF_ACTIVE; + + csicr3 = readl(pcdev->base_csi + CSICR3); + + /* Reflash DMA */ + writel(csicr3 | CSICR3_DMA_REFLASH_RFF, + pcdev->base_csi + CSICR3); + + /* clear & enable interrupts */ + writel(dma_inten, pcdev->base_csi + CSISR); + pcdev->csicr1 |= dma_inten; + writel(pcdev->csicr1, pcdev->base_csi + CSICR1); + + /* enable DMA */ + csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1); + writel(csicr3, pcdev->base_csi + CSICR3); + } + } + +out: + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void mx2_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + unsigned long flags; + +#ifdef DEBUG + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + switch (vb->state) { + case VIDEOBUF_ACTIVE: + dev_info(&icd->dev, "%s (active)\n", __func__); + break; + case VIDEOBUF_QUEUED: + dev_info(&icd->dev, "%s (queued)\n", __func__); + break; + case VIDEOBUF_PREPARED: + dev_info(&icd->dev, "%s (prepared)\n", __func__); + break; + default: + dev_info(&icd->dev, "%s (unknown) %d\n", __func__, + vb->state); + break; + } +#endif + + /* + * Terminate only queued but inactive buffers. Active buffers are + * released when they become inactive after videobuf_waiton(). + * + * FIXME: implement forced termination of active buffers, so that the + * user won't get stuck in an uninterruptible state. This requires a + * specific handling for each of the three DMA types that this driver + * supports. + */ + spin_lock_irqsave(&pcdev->lock, flags); + if (vb->state == VIDEOBUF_QUEUED) { + list_del(&vb->queue); + vb->state = VIDEOBUF_ERROR; + } + spin_unlock_irqrestore(&pcdev->lock, flags); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops mx2_videobuf_ops = { + .buf_setup = mx2_videobuf_setup, + .buf_prepare = mx2_videobuf_prepare, + .buf_queue = mx2_videobuf_queue, + .buf_release = mx2_videobuf_release, +}; + +static void mx2_camera_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + + videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev, + &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, sizeof(struct mx2_buffer), icd); +} + +#define MX2_BUS_FLAGS (SOCAM_DATAWIDTH_8 | \ + SOCAM_MASTER | \ + SOCAM_VSYNC_ACTIVE_HIGH | \ + SOCAM_VSYNC_ACTIVE_LOW | \ + SOCAM_HSYNC_ACTIVE_HIGH | \ + SOCAM_HSYNC_ACTIVE_LOW | \ + SOCAM_PCLK_SAMPLE_RISING | \ + SOCAM_PCLK_SAMPLE_FALLING | \ + SOCAM_DATA_ACTIVE_HIGH | \ + SOCAM_DATA_ACTIVE_LOW) + +static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +{ + u32 cntl; + int count = 0; + + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + while (count++ < 100) { + if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) + return 0; + barrier(); + udelay(1); + } + + return -ETIMEDOUT; +} + +static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, + int bytesperline) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + + writel(pcdev->discard_buffer_dma, + pcdev->base_emma + PRP_DEST_RGB1_PTR); + writel(pcdev->discard_buffer_dma, + pcdev->base_emma + PRP_DEST_RGB2_PTR); + + /* + * We only use the EMMA engine to get rid of the broken + * DMA Engine. No color space consversion at the moment. + * We adjust incoming and outgoing pixelformat to rgb16 + * and adjust the bytesperline accordingly. + */ + writel(PRP_CNTL_CH1EN | + PRP_CNTL_CSIEN | + PRP_CNTL_DATA_IN_RGB16 | + PRP_CNTL_CH1_OUT_RGB16 | + PRP_CNTL_CH1_LEN | + PRP_CNTL_CH1BYP | + PRP_CNTL_CH1_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + + writel(((bytesperline >> 1) << 16) | icd->user_height, + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + writel(((bytesperline >> 1) << 16) | icd->user_height, + pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); + writel(bytesperline, + pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); + writel(0x2ca00565, /* RGB565 */ + pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + writel(0x2ca00565, /* RGB565 */ + pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); + + /* Enable interrupts */ + writel(PRP_INTR_RDERR | + PRP_INTR_CH1WERR | + PRP_INTR_CH2WERR | + PRP_INTR_CH1FC | + PRP_INTR_CH2FC | + PRP_INTR_LBOVF | + PRP_INTR_CH2OVF, + pcdev->base_emma + PRP_INTR_CNTL); +} + +static int mx2_camera_set_bus_param(struct soc_camera_device *icd, + __u32 pixfmt) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + unsigned long camera_flags, common_flags; + int ret = 0; + int bytesperline; + u32 csicr1 = pcdev->csicr1; + + camera_flags = icd->ops->query_bus_param(icd); + + common_flags = soc_camera_bus_param_compatible(camera_flags, + MX2_BUS_FLAGS); + if (!common_flags) + return -EINVAL; + + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) + common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + else + common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING) + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + } + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + if (common_flags & SOCAM_PCLK_SAMPLE_RISING) + csicr1 |= CSICR1_REDGE; + if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + csicr1 |= CSICR1_INV_PCLK; + if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH) + csicr1 |= CSICR1_SOF_POL; + if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH) + csicr1 |= CSICR1_HSYNC_POL; + if (pcdev->platform_flags & MX2_CAMERA_SWAP16) + csicr1 |= CSICR1_SWAP16_EN; + if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) + csicr1 |= CSICR1_EXT_VSYNC; + if (pcdev->platform_flags & MX2_CAMERA_CCIR) + csicr1 |= CSICR1_CCIR_EN; + if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE) + csicr1 |= CSICR1_CCIR_MODE; + if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK) + csicr1 |= CSICR1_GCLK_MODE; + if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) + csicr1 |= CSICR1_INV_DATA; + if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB) + csicr1 |= CSICR1_PACK_DIR; + + pcdev->csicr1 = csicr1; + + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) + return bytesperline; + + if (mx27_camera_emma(pcdev)) { + ret = mx27_camera_emma_prp_reset(pcdev); + if (ret) + return ret; + + if (pcdev->discard_buffer) + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, pcdev->discard_buffer, + pcdev->discard_buffer_dma); + + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, &pcdev->discard_buffer_dma, + GFP_KERNEL); + if (!pcdev->discard_buffer) + return -ENOMEM; + + mx27_camera_emma_buf_init(icd, bytesperline); + } else if (cpu_is_mx25()) { + writel((bytesperline * icd->user_height) >> 2, + pcdev->base_csi + CSIRXCNT); + writel((bytesperline << 16) | icd->user_height, + pcdev->base_csi + CSIIMAG_PARA); + } + + writel(pcdev->csicr1, pcdev->base_csi + CSICR1); + + return 0; +} + +static int mx2_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_crop *a) +{ + struct v4l2_rect *rect = &a->c; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_framefmt mf; + int ret; + + soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); + soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); + + ret = v4l2_subdev_call(sd, video, s_crop, a); + if (ret < 0) + return ret; + + /* The capture device might have changed its output */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", + mf.width, mf.height); + + icd->user_width = mf.width; + icd->user_height = mf.height; + + return ret; +} + +static int mx2_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(icd->dev.parent, "Format %x not found\n", + pix->pixelformat); + return -EINVAL; + } + + /* eMMA can only do RGB565 */ + if (mx27_camera_emma(pcdev) && pix->pixelformat != V4L2_PIX_FMT_RGB565) + return -EINVAL; + + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + if (mf.code != xlate->code) + return -EINVAL; + + pix->width = mf.width; + pix->height = mf.height; + pix->field = mf.field; + pix->colorspace = mf.colorspace; + icd->current_fmt = xlate; + + return 0; +} + +static int mx2_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + __u32 pixfmt = pix->pixelformat; + unsigned int width_limit; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (pixfmt && !xlate) { + dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + /* FIXME: implement MX27 limits */ + + /* eMMA can only do RGB565 */ + if (mx27_camera_emma(pcdev) && pixfmt != V4L2_PIX_FMT_RGB565) + return -EINVAL; + + /* limit to MX25 hardware capabilities */ + if (cpu_is_mx25()) { + if (xlate->host_fmt->bits_per_sample <= 8) + width_limit = 0xffff * 4; + else + width_limit = 0xffff * 2; + /* CSIIMAG_PARA limit */ + if (pix->width > width_limit) + pix->width = width_limit; + if (pix->height > 0xffff) + pix->height = 0xffff; + + pix->bytesperline = soc_mbus_bytes_per_line(pix->width, + xlate->host_fmt); + if (pix->bytesperline < 0) + return pix->bytesperline; + pix->sizeimage = pix->height * pix->bytesperline; + if (pix->sizeimage > (4 * 0x3ffff)) { /* CSIRXCNT limit */ + dev_warn(icd->dev.parent, + "Image size (%u) above limit\n", + pix->sizeimage); + return -EINVAL; + } + } + + /* limit to sensor capabilities */ + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + if (ret < 0) + return ret; + + if (mf.field == V4L2_FIELD_ANY) + mf.field = V4L2_FIELD_NONE; + if (mf.field != V4L2_FIELD_NONE) { + dev_err(icd->dev.parent, "Field type %d unsupported.\n", + mf.field); + return -EINVAL; + } + + pix->width = mf.width; + pix->height = mf.height; + pix->field = mf.field; + pix->colorspace = mf.colorspace; + + return 0; +} + +static int mx2_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the friendly caller:-> */ + strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card)); + cap->version = MX2_CAM_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int mx2_camera_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + int i; + + for (i = 0; i < p->count; i++) { + struct mx2_buffer *buf = container_of(icf->vb_vidq.bufs[i], + struct mx2_buffer, vb); + INIT_LIST_HEAD(&buf->vb.queue); + } + + return 0; +} + +#ifdef CONFIG_MACH_MX27 +static void mx27_camera_frame_done(struct mx2_camera_dev *pcdev, int state) +{ + struct videobuf_buffer *vb; + struct mx2_buffer *buf; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pcdev->lock, flags); + + if (!pcdev->active) { + dev_err(pcdev->dev, "%s called with no active buffer!\n", + __func__); + goto out; + } + + vb = &pcdev->active->vb; + buf = container_of(vb, struct mx2_buffer, vb); + WARN_ON(list_empty(&vb->queue)); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ + list_del_init(&vb->queue); + vb->state = state; + do_gettimeofday(&vb->ts); + vb->field_count++; + + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + goto out; + } + + pcdev->active = list_entry(pcdev->capture.next, + struct mx2_buffer, vb.queue); + + vb = &pcdev->active->vb; + vb->state = VIDEOBUF_ACTIVE; + + ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb), + vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ); + + if (ret) { + vb->state = VIDEOBUF_ERROR; + pcdev->active = NULL; + wake_up(&vb->done); + } + +out: + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void mx27_camera_dma_err_callback(int channel, void *data, int err) +{ + struct mx2_camera_dev *pcdev = data; + + mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR); +} + +static void mx27_camera_dma_callback(int channel, void *data) +{ + struct mx2_camera_dev *pcdev = data; + + mx27_camera_frame_done(pcdev, VIDEOBUF_DONE); +} + +#define DMA_REQ_CSI_RX 31 /* FIXME: Add this to a resource */ + +static int __devinit mx27_camera_dma_init(struct platform_device *pdev, + struct mx2_camera_dev *pcdev) +{ + int err; + + pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH); + if (pcdev->dma < 0) { + dev_err(&pdev->dev, "%s failed to request DMA channel\n", + __func__); + return pcdev->dma; + } + + err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback, + mx27_camera_dma_err_callback, pcdev); + if (err) { + dev_err(&pdev->dev, "%s failed to set DMA callback\n", + __func__); + goto err_out; + } + + err = imx_dma_config_channel(pcdev->dma, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, + DMA_REQ_CSI_RX, 1); + if (err) { + dev_err(&pdev->dev, "%s failed to config DMA channel\n", + __func__); + goto err_out; + } + + imx_dma_config_burstlen(pcdev->dma, 64); + + return 0; + +err_out: + imx_dma_free(pcdev->dma); + + return err; +} +#endif /* CONFIG_MACH_MX27 */ + +static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + + return videobuf_poll_stream(file, &icf->vb_vidq, pt); +} + +static struct soc_camera_host_ops mx2_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = mx2_camera_add_device, + .remove = mx2_camera_remove_device, + .set_fmt = mx2_camera_set_fmt, + .set_crop = mx2_camera_set_crop, + .try_fmt = mx2_camera_try_fmt, + .init_videobuf = mx2_camera_init_videobuf, + .reqbufs = mx2_camera_reqbufs, + .poll = mx2_camera_poll, + .querycap = mx2_camera_querycap, + .set_bus_param = mx2_camera_set_bus_param, +}; + +static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, + int bufnum, int state) +{ + struct mx2_buffer *buf; + struct videobuf_buffer *vb; + unsigned long phys; + + if (!list_empty(&pcdev->active_bufs)) { + buf = list_entry(pcdev->active_bufs.next, + struct mx2_buffer, vb.queue); + + BUG_ON(buf->bufnum != bufnum); + + vb = &buf->vb; +#ifdef DEBUG + phys = videobuf_to_dma_contig(vb); + if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum) + != phys) { + dev_err(pcdev->dev, "%p != %p\n", phys, + readl(pcdev->base_emma + + PRP_DEST_RGB1_PTR + + 4 * bufnum)); + } +#endif + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, + vb->baddr, vb->bsize); + + list_del(&vb->queue); + vb->state = state; + do_gettimeofday(&vb->ts); + vb->field_count++; + + wake_up(&vb->done); + } + + if (list_empty(&pcdev->capture)) { + writel(pcdev->discard_buffer_dma, pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum); + return; + } + + buf = list_entry(pcdev->capture.next, + struct mx2_buffer, vb.queue); + + buf->bufnum = !bufnum; + + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + vb = &buf->vb; + vb->state = VIDEOBUF_ACTIVE; + + phys = videobuf_to_dma_contig(vb); + writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum); +} + +static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) +{ + struct mx2_camera_dev *pcdev = data; + unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); + struct mx2_buffer *buf; + + if (status & (1 << 7)) { /* overflow */ + u32 cntl; + /* + * We only disable channel 1 here since this is the only + * enabled channel + * + * FIXME: the correct DMA overflow handling should be resetting + * the buffer, returning an error frame, and continuing with + * the next one. + */ + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(cntl & ~PRP_CNTL_CH1EN, pcdev->base_emma + PRP_CNTL); + writel(cntl, pcdev->base_emma + PRP_CNTL); + } + if ((status & (3 << 5)) == (3 << 5) + && !list_empty(&pcdev->active_bufs)) { + /* + * Both buffers have triggered, process the one we're expecting + * to first + */ + buf = list_entry(pcdev->active_bufs.next, + struct mx2_buffer, vb.queue); + mx27_camera_frame_done_emma(pcdev, buf->bufnum, VIDEOBUF_DONE); + status &= ~(1 << (6 - buf->bufnum)); /* mark processed */ + } + if (status & (1 << 6)) + mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE); + if (status & (1 << 5)) + mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE); + + writel(status, pcdev->base_emma + PRP_INTRSTATUS); + + return IRQ_HANDLED; +} + +static int __devinit mx27_camera_emma_init(struct mx2_camera_dev *pcdev) +{ + struct resource *res_emma = pcdev->res_emma; + int err = 0; + + if (!request_mem_region(res_emma->start, resource_size(res_emma), + MX2_CAM_DRV_NAME)) { + err = -EBUSY; + goto out; + } + + pcdev->base_emma = ioremap(res_emma->start, resource_size(res_emma)); + if (!pcdev->base_emma) { + err = -ENOMEM; + goto exit_release; + } + + err = request_irq(pcdev->irq_emma, mx27_camera_emma_irq, 0, + MX2_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n"); + goto exit_iounmap; + } + + pcdev->clk_emma = clk_get(NULL, "emma"); + if (IS_ERR(pcdev->clk_emma)) { + err = PTR_ERR(pcdev->clk_emma); + goto exit_free_irq; + } + + clk_enable(pcdev->clk_emma); + + err = mx27_camera_emma_prp_reset(pcdev); + if (err) + goto exit_clk_emma_put; + + return err; + +exit_clk_emma_put: + clk_disable(pcdev->clk_emma); + clk_put(pcdev->clk_emma); +exit_free_irq: + free_irq(pcdev->irq_emma, pcdev); +exit_iounmap: + iounmap(pcdev->base_emma); +exit_release: + release_mem_region(res_emma->start, resource_size(res_emma)); +out: + return err; +} + +static int __devinit mx2_camera_probe(struct platform_device *pdev) +{ + struct mx2_camera_dev *pcdev; + struct resource *res_csi, *res_emma; + void __iomem *base_csi; + int irq_csi, irq_emma; + irq_handler_t mx2_cam_irq_handler = cpu_is_mx25() ? mx25_camera_irq + : mx27_camera_irq; + int err = 0; + + dev_dbg(&pdev->dev, "initialising\n"); + + res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_csi = platform_get_irq(pdev, 0); + if (res_csi == NULL || irq_csi < 0) { + dev_err(&pdev->dev, "Missing platform resources data\n"); + err = -ENODEV; + goto exit; + } + + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + dev_err(&pdev->dev, "Could not allocate pcdev\n"); + err = -ENOMEM; + goto exit; + } + + pcdev->clk_csi = clk_get(&pdev->dev, NULL); + if (IS_ERR(pcdev->clk_csi)) { + err = PTR_ERR(pcdev->clk_csi); + goto exit_kfree; + } + + dev_dbg(&pdev->dev, "Camera clock frequency: %ld\n", + clk_get_rate(pcdev->clk_csi)); + + /* Initialize DMA */ +#ifdef CONFIG_MACH_MX27 + if (cpu_is_mx27()) { + err = mx27_camera_dma_init(pdev, pcdev); + if (err) + goto exit_clk_put; + } +#endif /* CONFIG_MACH_MX27 */ + + pcdev->res_csi = res_csi; + pcdev->pdata = pdev->dev.platform_data; + if (pcdev->pdata) { + long rate; + + pcdev->platform_flags = pcdev->pdata->flags; + + rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2); + if (rate <= 0) { + err = -ENODEV; + goto exit_dma_free; + } + err = clk_set_rate(pcdev->clk_csi, rate); + if (err < 0) + goto exit_dma_free; + } + + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + spin_lock_init(&pcdev->lock); + + /* + * Request the regions. + */ + if (!request_mem_region(res_csi->start, resource_size(res_csi), + MX2_CAM_DRV_NAME)) { + err = -EBUSY; + goto exit_dma_free; + } + + base_csi = ioremap(res_csi->start, resource_size(res_csi)); + if (!base_csi) { + err = -ENOMEM; + goto exit_release; + } + pcdev->irq_csi = irq_csi; + pcdev->base_csi = base_csi; + pcdev->base_dma = res_csi->start; + pcdev->dev = &pdev->dev; + + err = request_irq(pcdev->irq_csi, mx2_cam_irq_handler, 0, + MX2_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_iounmap; + } + + if (cpu_is_mx27()) { + /* EMMA support */ + res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq_emma = platform_get_irq(pdev, 1); + + if (res_emma && irq_emma >= 0) { + dev_info(&pdev->dev, "Using EMMA\n"); + pcdev->use_emma = 1; + pcdev->res_emma = res_emma; + pcdev->irq_emma = irq_emma; + if (mx27_camera_emma_init(pcdev)) + goto exit_free_irq; + } + } + + pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, + pcdev->soc_host.ops = &mx2_soc_camera_host_ops, + pcdev->soc_host.priv = pcdev; + pcdev->soc_host.v4l2_dev.dev = &pdev->dev; + pcdev->soc_host.nr = pdev->id; + err = soc_camera_host_register(&pcdev->soc_host); + if (err) + goto exit_free_emma; + + return 0; + +exit_free_emma: + if (mx27_camera_emma(pcdev)) { + free_irq(pcdev->irq_emma, pcdev); + clk_disable(pcdev->clk_emma); + clk_put(pcdev->clk_emma); + iounmap(pcdev->base_emma); + release_mem_region(res_emma->start, resource_size(res_emma)); + } +exit_free_irq: + free_irq(pcdev->irq_csi, pcdev); +exit_iounmap: + iounmap(base_csi); +exit_release: + release_mem_region(res_csi->start, resource_size(res_csi)); +exit_dma_free: +#ifdef CONFIG_MACH_MX27 + if (cpu_is_mx27()) + imx_dma_free(pcdev->dma); +exit_clk_put: + clk_put(pcdev->clk_csi); +#endif /* CONFIG_MACH_MX27 */ +exit_kfree: + kfree(pcdev); +exit: + return err; +} + +static int __devexit mx2_camera_remove(struct platform_device *pdev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct mx2_camera_dev *pcdev = container_of(soc_host, + struct mx2_camera_dev, soc_host); + struct resource *res; + + clk_put(pcdev->clk_csi); +#ifdef CONFIG_MACH_MX27 + if (cpu_is_mx27()) + imx_dma_free(pcdev->dma); +#endif /* CONFIG_MACH_MX27 */ + free_irq(pcdev->irq_csi, pcdev); + if (mx27_camera_emma(pcdev)) + free_irq(pcdev->irq_emma, pcdev); + + soc_camera_host_unregister(&pcdev->soc_host); + + iounmap(pcdev->base_csi); + + if (mx27_camera_emma(pcdev)) { + clk_disable(pcdev->clk_emma); + clk_put(pcdev->clk_emma); + iounmap(pcdev->base_emma); + res = pcdev->res_emma; + release_mem_region(res->start, resource_size(res)); + } + + res = pcdev->res_csi; + release_mem_region(res->start, resource_size(res)); + + kfree(pcdev); + + dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver mx2_camera_driver = { + .driver = { + .name = MX2_CAM_DRV_NAME, + }, + .remove = __devexit_p(mx2_camera_remove), +}; + + +static int __init mx2_camera_init(void) +{ + return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe); +} + +static void __exit mx2_camera_exit(void) +{ + return platform_driver_unregister(&mx2_camera_driver); +} + +module_init(mx2_camera_init); +module_exit(mx2_camera_exit); + +MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver"); +MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c index 929073e792c..4ed51b1552e 100644 --- a/drivers/media/video/omap/omap_vout.c +++ b/drivers/media/video/omap/omap_vout.c @@ -2545,19 +2545,11 @@ static int __init omap_vout_probe(struct platform_device *pdev) /* set the update mode */ if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { -#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE - if (dssdrv->enable_te) - dssdrv->enable_te(def_display, 1); - if (dssdrv->set_update_mode) - dssdrv->set_update_mode(def_display, - OMAP_DSS_UPDATE_AUTO); -#else /* MANUAL_UPDATE */ if (dssdrv->enable_te) dssdrv->enable_te(def_display, 0); if (dssdrv->set_update_mode) dssdrv->set_update_mode(def_display, OMAP_DSS_UPDATE_MANUAL); -#endif } else { if (dssdrv->set_update_mode) dssdrv->set_update_mode(def_display, diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index f85b2ed8a2d..926a5aa6f7f 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -426,7 +426,7 @@ static void omap24xxcam_vbq_release(struct videobuf_queue *vbq, dma->direction); dma->direction = DMA_NONE; } else { - videobuf_dma_unmap(vbq, videobuf_to_dma(vb)); + videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb)); videobuf_dma_free(videobuf_to_dma(vb)); } diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c deleted file mode 100644 index a10912097b7..00000000000 --- a/drivers/media/video/ov511.c +++ /dev/null @@ -1,5995 +0,0 @@ -/* - * OmniVision OV511 Camera-to-USB Bridge Driver - * - * Copyright (c) 1999-2003 Mark W. McClelland - * Original decompression code Copyright 1998-2000 OmniVision Technologies - * Many improvements by Bret Wallach <bwallac1@san.rr.com> - * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) - * Snapshot code by Kevin Moore - * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> - * Changes by Claudio Matsuoka <claudio@conectiva.com> - * Original SAA7111A code by Dave Perks <dperks@ibm.net> - * URB error messages from pwc driver by Nemosoft - * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox - * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others - * - * Based on the Linux CPiA driver written by Peter Pregler, - * Scott J. Bertin and Johannes Erdfelt. - * - * Please see the file: Documentation/usb/ov511.txt - * and the website at: http://alpha.dyndns.org/ov511 - * for more info. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/ctype.h> -#include <linux/pagemap.h> -#include <asm/processor.h> -#include <linux/mm.h> -#include <linux/device.h> - -#if defined (__i386__) - #include <asm/cpufeature.h> -#endif - -#include "ov511.h" - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.64 for Linux 2.5" -#define EMAIL "mark@alpha.dyndns.org" -#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \ -& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \ -<cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>" -#define DRIVER_DESC "ov511 USB Camera Driver" - -#define OV511_I2C_RETRIES 3 -#define ENABLE_Y_QUANTABLE 1 -#define ENABLE_UV_QUANTABLE 1 - -#define OV511_MAX_UNIT_VIDEO 16 - -/* Pixel count * bytes per YUV420 pixel (1.5) */ -#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2) - -#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval)) - -/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */ -#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024) - -#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) - -/********************************************************************** - * Module Parameters - * (See ov511.txt for detailed descriptions of these) - **********************************************************************/ - -/* These variables (and all static globals) default to zero */ -static int autobright = 1; -static int autogain = 1; -static int autoexp = 1; -static int debug; -static int snapshot; -static int cams = 1; -static int compress; -static int testpat; -static int dumppix; -static int led = 1; -static int dump_bridge; -static int dump_sensor; -static int printph; -static int phy = 0x1f; -static int phuv = 0x05; -static int pvy = 0x06; -static int pvuv = 0x06; -static int qhy = 0x14; -static int qhuv = 0x03; -static int qvy = 0x04; -static int qvuv = 0x04; -static int lightfreq; -static int bandingfilter; -static int clockdiv = -1; -static int packetsize = -1; -static int framedrop = -1; -static int fastset; -static int force_palette; -static int backlight; -/* Bitmask marking allocated devices from 0 to OV511_MAX_UNIT_VIDEO */ -static unsigned long ov511_devused; -static int unit_video[OV511_MAX_UNIT_VIDEO]; -static int remove_zeros; -static int mirror; -static int ov518_color; - -module_param(autobright, int, 0); -MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness"); -module_param(autogain, int, 0); -MODULE_PARM_DESC(autogain, "Sensor automatically changes gain"); -module_param(autoexp, int, 0); -MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure"); -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, - "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max"); -module_param(snapshot, int, 0); -MODULE_PARM_DESC(snapshot, "Enable snapshot mode"); -module_param(cams, int, 0); -MODULE_PARM_DESC(cams, "Number of simultaneous cameras"); -module_param(compress, int, 0); -MODULE_PARM_DESC(compress, "Turn on compression"); -module_param(testpat, int, 0); -MODULE_PARM_DESC(testpat, - "Replace image with vertical bar testpattern (only partially working)"); -module_param(dumppix, int, 0); -MODULE_PARM_DESC(dumppix, "Dump raw pixel data"); -module_param(led, int, 0); -MODULE_PARM_DESC(led, - "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)"); -module_param(dump_bridge, int, 0); -MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers"); -module_param(dump_sensor, int, 0); -MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers"); -module_param(printph, int, 0); -MODULE_PARM_DESC(printph, "Print frame start/end headers"); -module_param(phy, int, 0); -MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)"); -module_param(phuv, int, 0); -MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)"); -module_param(pvy, int, 0); -MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)"); -module_param(pvuv, int, 0); -MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)"); -module_param(qhy, int, 0); -MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)"); -module_param(qhuv, int, 0); -MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)"); -module_param(qvy, int, 0); -MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)"); -module_param(qvuv, int, 0); -MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)"); -module_param(lightfreq, int, 0); -MODULE_PARM_DESC(lightfreq, - "Light frequency. Set to 50 or 60 Hz, or zero for default settings"); -module_param(bandingfilter, int, 0); -MODULE_PARM_DESC(bandingfilter, - "Enable banding filter (to reduce effects of fluorescent lighting)"); -module_param(clockdiv, int, 0); -MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value"); -module_param(packetsize, int, 0); -MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size"); -module_param(framedrop, int, 0); -MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting"); -module_param(fastset, int, 0); -MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately"); -module_param(force_palette, int, 0); -MODULE_PARM_DESC(force_palette, "Force the palette to a specific value"); -module_param(backlight, int, 0); -MODULE_PARM_DESC(backlight, "For objects that are lit from behind"); -static unsigned int num_uv; -module_param_array(unit_video, int, &num_uv, 0); -MODULE_PARM_DESC(unit_video, - "Force use of specific minor number(s). 0 is not allowed."); -module_param(remove_zeros, int, 0); -MODULE_PARM_DESC(remove_zeros, - "Remove zero-padding from uncompressed incoming data"); -module_param(mirror, int, 0); -MODULE_PARM_DESC(mirror, "Reverse image horizontally"); -module_param(ov518_color, int, 0); -MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)"); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -/********************************************************************** - * Miscellaneous Globals - **********************************************************************/ - -static struct usb_driver ov511_driver; - -/* Number of times to retry a failed I2C transaction. Increase this if you - * are getting "Failed to read sensor ID..." */ -static const int i2c_detect_tries = 5; - -static struct usb_device_id device_table [] = { - { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, - { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, device_table); - -static unsigned char yQuanTable511[] = OV511_YQUANTABLE; -static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE; -static unsigned char yQuanTable518[] = OV518_YQUANTABLE; -static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE; - -/********************************************************************** - * Symbolic Names - **********************************************************************/ - -/* Known OV511-based cameras */ -static struct symbolic_list camlist[] = { - { 0, "Generic Camera (no ID)" }, - { 1, "Mustek WCam 3X" }, - { 3, "D-Link DSB-C300" }, - { 4, "Generic OV511/OV7610" }, - { 5, "Puretek PT-6007" }, - { 6, "Lifeview USB Life TV (NTSC)" }, - { 21, "Creative Labs WebCam 3" }, - { 22, "Lifeview USB Life TV (PAL D/K+B/G)" }, - { 36, "Koala-Cam" }, - { 38, "Lifeview USB Life TV (PAL)" }, - { 41, "Samsung Anycam MPC-M10" }, - { 43, "Mtekvision Zeca MV402" }, - { 46, "Suma eON" }, - { 70, "Lifeview USB Life TV (PAL/SECAM)" }, - { 100, "Lifeview RoboCam" }, - { 102, "AverMedia InterCam Elite" }, - { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ - { 134, "Ezonics EZCam II" }, - { 192, "Webeye 2000B" }, - { 253, "Alpha Vision Tech. AlphaCam SE" }, - { -1, NULL } -}; - -/* Video4Linux1 Palettes */ -static struct symbolic_list v4l1_plist[] = { - { VIDEO_PALETTE_GREY, "GREY" }, - { VIDEO_PALETTE_HI240, "HI240" }, - { VIDEO_PALETTE_RGB565, "RGB565" }, - { VIDEO_PALETTE_RGB24, "RGB24" }, - { VIDEO_PALETTE_RGB32, "RGB32" }, - { VIDEO_PALETTE_RGB555, "RGB555" }, - { VIDEO_PALETTE_YUV422, "YUV422" }, - { VIDEO_PALETTE_YUYV, "YUYV" }, - { VIDEO_PALETTE_UYVY, "UYVY" }, - { VIDEO_PALETTE_YUV420, "YUV420" }, - { VIDEO_PALETTE_YUV411, "YUV411" }, - { VIDEO_PALETTE_RAW, "RAW" }, - { VIDEO_PALETTE_YUV422P,"YUV422P" }, - { VIDEO_PALETTE_YUV411P,"YUV411P" }, - { VIDEO_PALETTE_YUV420P,"YUV420P" }, - { VIDEO_PALETTE_YUV410P,"YUV410P" }, - { -1, NULL } -}; - -static struct symbolic_list brglist[] = { - { BRG_OV511, "OV511" }, - { BRG_OV511PLUS, "OV511+" }, - { BRG_OV518, "OV518" }, - { BRG_OV518PLUS, "OV518+" }, - { -1, NULL } -}; - -static struct symbolic_list senlist[] = { - { SEN_OV76BE, "OV76BE" }, - { SEN_OV7610, "OV7610" }, - { SEN_OV7620, "OV7620" }, - { SEN_OV7620AE, "OV7620AE" }, - { SEN_OV6620, "OV6620" }, - { SEN_OV6630, "OV6630" }, - { SEN_OV6630AE, "OV6630AE" }, - { SEN_OV6630AF, "OV6630AF" }, - { SEN_OV8600, "OV8600" }, - { SEN_KS0127, "KS0127" }, - { SEN_KS0127B, "KS0127B" }, - { SEN_SAA7111A, "SAA7111A" }, - { -1, NULL } -}; - -/* URB error codes: */ -static struct symbolic_list urb_errlist[] = { - { -ENOSR, "Buffer error (overrun)" }, - { -EPIPE, "Stalled (device not responding)" }, - { -EOVERFLOW, "Babble (device sends too much data)" }, - { -EPROTO, "Bit-stuff error (bad cable?)" }, - { -EILSEQ, "CRC/Timeout (bad cable?)" }, - { -ETIME, "Device does not respond to token" }, - { -ETIMEDOUT, "Device does not respond to command" }, - { -1, NULL } -}; - -/********************************************************************** - * Memory management - **********************************************************************/ -static void * -rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void -rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/********************************************************************** - * - * Register I/O - * - **********************************************************************/ - -/* Write an OV51x register */ -static int -reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) -{ - int rc; - - PDEBUG(5, "0x%02X:0x%02X", reg, value); - - mutex_lock(&ov->cbuf_lock); - ov->cbuf[0] = value; - rc = usb_control_msg(ov->dev, - usb_sndctrlpipe(ov->dev, 0), - (ov->bclass == BCL_OV518)?1:2 /* REG_IO */, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, (__u16)reg, &ov->cbuf[0], 1, 1000); - mutex_unlock(&ov->cbuf_lock); - - if (rc < 0) - err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc)); - - return rc; -} - -/* Read from an OV51x register */ -/* returns: negative is error, pos or zero is data */ -static int -reg_r(struct usb_ov511 *ov, unsigned char reg) -{ - int rc; - - mutex_lock(&ov->cbuf_lock); - rc = usb_control_msg(ov->dev, - usb_rcvctrlpipe(ov->dev, 0), - (ov->bclass == BCL_OV518)?1:3 /* REG_IO */, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, (__u16)reg, &ov->cbuf[0], 1, 1000); - - if (rc < 0) { - err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc)); - } else { - rc = ov->cbuf[0]; - PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); - } - - mutex_unlock(&ov->cbuf_lock); - - return rc; -} - -/* - * Writes bits at positions specified by mask to an OV51x reg. Bits that are in - * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless - * of their respective state in "value". - */ -static int -reg_w_mask(struct usb_ov511 *ov, - unsigned char reg, - unsigned char value, - unsigned char mask) -{ - int ret; - unsigned char oldval, newval; - - ret = reg_r(ov, reg); - if (ret < 0) - return ret; - - oldval = (unsigned char) ret; - oldval &= (~mask); /* Clear the masked bits */ - value &= mask; /* Enforce mask on value */ - newval = oldval | value; /* Set the desired bits */ - - return (reg_w(ov, reg, newval)); -} - -/* - * Writes multiple (n) byte value to a single register. Only valid with certain - * registers (0x30 and 0xc4 - 0xce). - */ -static int -ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) -{ - int rc; - - PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n); - - mutex_lock(&ov->cbuf_lock); - - *((__le32 *)ov->cbuf) = __cpu_to_le32(val); - - rc = usb_control_msg(ov->dev, - usb_sndctrlpipe(ov->dev, 0), - 1 /* REG_IO */, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, (__u16)reg, ov->cbuf, n, 1000); - mutex_unlock(&ov->cbuf_lock); - - if (rc < 0) - err("reg write multiple: error %d: %s", rc, - symbolic(urb_errlist, rc)); - - return rc; -} - -static int -ov511_upload_quan_tables(struct usb_ov511 *ov) -{ - unsigned char *pYTable = yQuanTable511; - unsigned char *pUVTable = uvQuanTable511; - unsigned char val0, val1; - int i, rc, reg = R511_COMP_LUT_BEGIN; - - PDEBUG(4, "Uploading quantization tables"); - - for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) { - if (ENABLE_Y_QUANTABLE) { - val0 = *pYTable++; - val1 = *pYTable++; - val0 &= 0x0f; - val1 &= 0x0f; - val0 |= val1 << 4; - rc = reg_w(ov, reg, val0); - if (rc < 0) - return rc; - } - - if (ENABLE_UV_QUANTABLE) { - val0 = *pUVTable++; - val1 = *pUVTable++; - val0 &= 0x0f; - val1 &= 0x0f; - val0 |= val1 << 4; - rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0); - if (rc < 0) - return rc; - } - - reg++; - } - - return 0; -} - -/* OV518 quantization tables are 8x4 (instead of 8x8) */ -static int -ov518_upload_quan_tables(struct usb_ov511 *ov) -{ - unsigned char *pYTable = yQuanTable518; - unsigned char *pUVTable = uvQuanTable518; - unsigned char val0, val1; - int i, rc, reg = R511_COMP_LUT_BEGIN; - - PDEBUG(4, "Uploading quantization tables"); - - for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) { - if (ENABLE_Y_QUANTABLE) { - val0 = *pYTable++; - val1 = *pYTable++; - val0 &= 0x0f; - val1 &= 0x0f; - val0 |= val1 << 4; - rc = reg_w(ov, reg, val0); - if (rc < 0) - return rc; - } - - if (ENABLE_UV_QUANTABLE) { - val0 = *pUVTable++; - val1 = *pUVTable++; - val0 &= 0x0f; - val1 &= 0x0f; - val0 |= val1 << 4; - rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0); - if (rc < 0) - return rc; - } - - reg++; - } - - return 0; -} - -static int -ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) -{ - int rc; - - /* Setting bit 0 not allowed on 518/518Plus */ - if (ov->bclass == BCL_OV518) - reset_type &= 0xfe; - - PDEBUG(4, "Reset: type=0x%02X", reset_type); - - rc = reg_w(ov, R51x_SYS_RESET, reset_type); - rc = reg_w(ov, R51x_SYS_RESET, 0); - - if (rc < 0) - err("reset: command failed"); - - return rc; -} - -/********************************************************************** - * - * Low-level I2C I/O functions - * - **********************************************************************/ - -/* NOTE: Do not call this function directly! - * The OV518 I2C I/O procedure is different, hence, this function. - * This is normally only called from i2c_w(). Note that this function - * always succeeds regardless of whether the sensor is present and working. - */ -static int -ov518_i2c_write_internal(struct usb_ov511 *ov, - unsigned char reg, - unsigned char value) -{ - int rc; - - PDEBUG(5, "0x%02X:0x%02X", reg, value); - - /* Select camera register */ - rc = reg_w(ov, R51x_I2C_SADDR_3, reg); - if (rc < 0) - return rc; - - /* Write "value" to I2C data port of OV511 */ - rc = reg_w(ov, R51x_I2C_DATA, value); - if (rc < 0) - return rc; - - /* Initiate 3-byte write cycle */ - rc = reg_w(ov, R518_I2C_CTL, 0x01); - if (rc < 0) - return rc; - - return 0; -} - -/* NOTE: Do not call this function directly! */ -static int -ov511_i2c_write_internal(struct usb_ov511 *ov, - unsigned char reg, - unsigned char value) -{ - int rc, retries; - - PDEBUG(5, "0x%02X:0x%02X", reg, value); - - /* Three byte write cycle */ - for (retries = OV511_I2C_RETRIES; ; ) { - /* Select camera register */ - rc = reg_w(ov, R51x_I2C_SADDR_3, reg); - if (rc < 0) - break; - - /* Write "value" to I2C data port of OV511 */ - rc = reg_w(ov, R51x_I2C_DATA, value); - if (rc < 0) - break; - - /* Initiate 3-byte write cycle */ - rc = reg_w(ov, R511_I2C_CTL, 0x01); - if (rc < 0) - break; - - /* Retry until idle */ - do { - rc = reg_r(ov, R511_I2C_CTL); - } while (rc > 0 && ((rc&1) == 0)); - if (rc < 0) - break; - - /* Ack? */ - if ((rc&2) == 0) { - rc = 0; - break; - } -#if 0 - /* I2C abort */ - reg_w(ov, R511_I2C_CTL, 0x10); -#endif - if (--retries < 0) { - err("i2c write retries exhausted"); - rc = -1; - break; - } - } - - return rc; -} - -/* NOTE: Do not call this function directly! - * The OV518 I2C I/O procedure is different, hence, this function. - * This is normally only called from i2c_r(). Note that this function - * always succeeds regardless of whether the sensor is present and working. - */ -static int -ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) -{ - int rc, value; - - /* Select camera register */ - rc = reg_w(ov, R51x_I2C_SADDR_2, reg); - if (rc < 0) - return rc; - - /* Initiate 2-byte write cycle */ - rc = reg_w(ov, R518_I2C_CTL, 0x03); - if (rc < 0) - return rc; - - /* Initiate 2-byte read cycle */ - rc = reg_w(ov, R518_I2C_CTL, 0x05); - if (rc < 0) - return rc; - - value = reg_r(ov, R51x_I2C_DATA); - - PDEBUG(5, "0x%02X:0x%02X", reg, value); - - return value; -} - -/* NOTE: Do not call this function directly! - * returns: negative is error, pos or zero is data */ -static int -ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) -{ - int rc, value, retries; - - /* Two byte write cycle */ - for (retries = OV511_I2C_RETRIES; ; ) { - /* Select camera register */ - rc = reg_w(ov, R51x_I2C_SADDR_2, reg); - if (rc < 0) - return rc; - - /* Initiate 2-byte write cycle */ - rc = reg_w(ov, R511_I2C_CTL, 0x03); - if (rc < 0) - return rc; - - /* Retry until idle */ - do { - rc = reg_r(ov, R511_I2C_CTL); - } while (rc > 0 && ((rc & 1) == 0)); - if (rc < 0) - return rc; - - if ((rc&2) == 0) /* Ack? */ - break; - - /* I2C abort */ - reg_w(ov, R511_I2C_CTL, 0x10); - - if (--retries < 0) { - err("i2c write retries exhausted"); - return -1; - } - } - - /* Two byte read cycle */ - for (retries = OV511_I2C_RETRIES; ; ) { - /* Initiate 2-byte read cycle */ - rc = reg_w(ov, R511_I2C_CTL, 0x05); - if (rc < 0) - return rc; - - /* Retry until idle */ - do { - rc = reg_r(ov, R511_I2C_CTL); - } while (rc > 0 && ((rc&1) == 0)); - if (rc < 0) - return rc; - - if ((rc&2) == 0) /* Ack? */ - break; - - /* I2C abort */ - rc = reg_w(ov, R511_I2C_CTL, 0x10); - if (rc < 0) - return rc; - - if (--retries < 0) { - err("i2c read retries exhausted"); - return -1; - } - } - - value = reg_r(ov, R51x_I2C_DATA); - - PDEBUG(5, "0x%02X:0x%02X", reg, value); - - /* This is needed to make i2c_w() work */ - rc = reg_w(ov, R511_I2C_CTL, 0x05); - if (rc < 0) - return rc; - - return value; -} - -/* returns: negative is error, pos or zero is data */ -static int -i2c_r(struct usb_ov511 *ov, unsigned char reg) -{ - int rc; - - mutex_lock(&ov->i2c_lock); - - if (ov->bclass == BCL_OV518) - rc = ov518_i2c_read_internal(ov, reg); - else - rc = ov511_i2c_read_internal(ov, reg); - - mutex_unlock(&ov->i2c_lock); - - return rc; -} - -static int -i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) -{ - int rc; - - mutex_lock(&ov->i2c_lock); - - if (ov->bclass == BCL_OV518) - rc = ov518_i2c_write_internal(ov, reg, value); - else - rc = ov511_i2c_write_internal(ov, reg, value); - - mutex_unlock(&ov->i2c_lock); - - return rc; -} - -/* Do not call this function directly! */ -static int -ov51x_i2c_write_mask_internal(struct usb_ov511 *ov, - unsigned char reg, - unsigned char value, - unsigned char mask) -{ - int rc; - unsigned char oldval, newval; - - if (mask == 0xff) { - newval = value; - } else { - if (ov->bclass == BCL_OV518) - rc = ov518_i2c_read_internal(ov, reg); - else - rc = ov511_i2c_read_internal(ov, reg); - if (rc < 0) - return rc; - - oldval = (unsigned char) rc; - oldval &= (~mask); /* Clear the masked bits */ - value &= mask; /* Enforce mask on value */ - newval = oldval | value; /* Set the desired bits */ - } - - if (ov->bclass == BCL_OV518) - return (ov518_i2c_write_internal(ov, reg, newval)); - else - return (ov511_i2c_write_internal(ov, reg, newval)); -} - -/* Writes bits at positions specified by mask to an I2C reg. Bits that are in - * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless - * of their respective state in "value". - */ -static int -i2c_w_mask(struct usb_ov511 *ov, - unsigned char reg, - unsigned char value, - unsigned char mask) -{ - int rc; - - mutex_lock(&ov->i2c_lock); - rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); - mutex_unlock(&ov->i2c_lock); - - return rc; -} - -/* Set the read and write slave IDs. The "slave" argument is the write slave, - * and the read slave will be set to (slave + 1). ov->i2c_lock should be held - * when calling this. This should not be called from outside the i2c I/O - * functions. - */ -static int -i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave) -{ - int rc; - - rc = reg_w(ov, R51x_I2C_W_SID, slave); - if (rc < 0) - return rc; - - rc = reg_w(ov, R51x_I2C_R_SID, slave + 1); - if (rc < 0) - return rc; - - return 0; -} - -/* Write to a specific I2C slave ID and register, using the specified mask */ -static int -i2c_w_slave(struct usb_ov511 *ov, - unsigned char slave, - unsigned char reg, - unsigned char value, - unsigned char mask) -{ - int rc = 0; - - mutex_lock(&ov->i2c_lock); - - /* Set new slave IDs */ - rc = i2c_set_slave_internal(ov, slave); - if (rc < 0) - goto out; - - rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); - -out: - /* Restore primary IDs */ - if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) - err("Couldn't restore primary I2C slave"); - - mutex_unlock(&ov->i2c_lock); - return rc; -} - -/* Read from a specific I2C slave ID and register */ -static int -i2c_r_slave(struct usb_ov511 *ov, - unsigned char slave, - unsigned char reg) -{ - int rc; - - mutex_lock(&ov->i2c_lock); - - /* Set new slave IDs */ - rc = i2c_set_slave_internal(ov, slave); - if (rc < 0) - goto out; - - if (ov->bclass == BCL_OV518) - rc = ov518_i2c_read_internal(ov, reg); - else - rc = ov511_i2c_read_internal(ov, reg); - -out: - /* Restore primary IDs */ - if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) - err("Couldn't restore primary I2C slave"); - - mutex_unlock(&ov->i2c_lock); - return rc; -} - -/* Sets I2C read and write slave IDs. Returns <0 for error */ -static int -ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) -{ - int rc; - - mutex_lock(&ov->i2c_lock); - - rc = i2c_set_slave_internal(ov, sid); - if (rc < 0) - goto out; - - // FIXME: Is this actually necessary? - rc = ov51x_reset(ov, OV511_RESET_NOREGS); -out: - mutex_unlock(&ov->i2c_lock); - return rc; -} - -static int -write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals) -{ - int rc; - - while (pRegvals->bus != OV511_DONE_BUS) { - if (pRegvals->bus == OV511_REG_BUS) { - if ((rc = reg_w(ov, pRegvals->reg, pRegvals->val)) < 0) - return rc; - } else if (pRegvals->bus == OV511_I2C_BUS) { - if ((rc = i2c_w(ov, pRegvals->reg, pRegvals->val)) < 0) - return rc; - } else { - err("Bad regval array"); - return -1; - } - pRegvals++; - } - return 0; -} - -#ifdef OV511_DEBUG -static void -dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) -{ - int i, rc; - - for (i = reg1; i <= regn; i++) { - rc = i2c_r(ov, i); - dev_info(&ov->dev->dev, "Sensor[0x%02X] = 0x%02X\n", i, rc); - } -} - -static void -dump_i2c_regs(struct usb_ov511 *ov) -{ - dev_info(&ov->dev->dev, "I2C REGS\n"); - dump_i2c_range(ov, 0x00, 0x7C); -} - -static void -dump_reg_range(struct usb_ov511 *ov, int reg1, int regn) -{ - int i, rc; - - for (i = reg1; i <= regn; i++) { - rc = reg_r(ov, i); - dev_info(&ov->dev->dev, "OV511[0x%02X] = 0x%02X\n", i, rc); - } -} - -static void -ov511_dump_regs(struct usb_ov511 *ov) -{ - dev_info(&ov->dev->dev, "CAMERA INTERFACE REGS\n"); - dump_reg_range(ov, 0x10, 0x1f); - dev_info(&ov->dev->dev, "DRAM INTERFACE REGS\n"); - dump_reg_range(ov, 0x20, 0x23); - dev_info(&ov->dev->dev, "ISO FIFO REGS\n"); - dump_reg_range(ov, 0x30, 0x31); - dev_info(&ov->dev->dev, "PIO REGS\n"); - dump_reg_range(ov, 0x38, 0x39); - dump_reg_range(ov, 0x3e, 0x3e); - dev_info(&ov->dev->dev, "I2C REGS\n"); - dump_reg_range(ov, 0x40, 0x49); - dev_info(&ov->dev->dev, "SYSTEM CONTROL REGS\n"); - dump_reg_range(ov, 0x50, 0x55); - dump_reg_range(ov, 0x5e, 0x5f); - dev_info(&ov->dev->dev, "OmniCE REGS\n"); - dump_reg_range(ov, 0x70, 0x79); - /* NOTE: Quantization tables are not readable. You will get the value - * in reg. 0x79 for every table register */ - dump_reg_range(ov, 0x80, 0x9f); - dump_reg_range(ov, 0xa0, 0xbf); - -} - -static void -ov518_dump_regs(struct usb_ov511 *ov) -{ - dev_info(&ov->dev->dev, "VIDEO MODE REGS\n"); - dump_reg_range(ov, 0x20, 0x2f); - dev_info(&ov->dev->dev, "DATA PUMP AND SNAPSHOT REGS\n"); - dump_reg_range(ov, 0x30, 0x3f); - dev_info(&ov->dev->dev, "I2C REGS\n"); - dump_reg_range(ov, 0x40, 0x4f); - dev_info(&ov->dev->dev, "SYSTEM CONTROL AND VENDOR REGS\n"); - dump_reg_range(ov, 0x50, 0x5f); - dev_info(&ov->dev->dev, "60 - 6F\n"); - dump_reg_range(ov, 0x60, 0x6f); - dev_info(&ov->dev->dev, "70 - 7F\n"); - dump_reg_range(ov, 0x70, 0x7f); - dev_info(&ov->dev->dev, "Y QUANTIZATION TABLE\n"); - dump_reg_range(ov, 0x80, 0x8f); - dev_info(&ov->dev->dev, "UV QUANTIZATION TABLE\n"); - dump_reg_range(ov, 0x90, 0x9f); - dev_info(&ov->dev->dev, "A0 - BF\n"); - dump_reg_range(ov, 0xa0, 0xbf); - dev_info(&ov->dev->dev, "CBR\n"); - dump_reg_range(ov, 0xc0, 0xcf); -} -#endif - -/*****************************************************************************/ - -/* Temporarily stops OV511 from functioning. Must do this before changing - * registers while the camera is streaming */ -static inline int -ov51x_stop(struct usb_ov511 *ov) -{ - PDEBUG(4, "stopping"); - ov->stopped = 1; - if (ov->bclass == BCL_OV518) - return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a)); - else - return (reg_w(ov, R51x_SYS_RESET, 0x3d)); -} - -/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not - * actually stopped (for performance). */ -static inline int -ov51x_restart(struct usb_ov511 *ov) -{ - if (ov->stopped) { - PDEBUG(4, "restarting"); - ov->stopped = 0; - - /* Reinitialize the stream */ - if (ov->bclass == BCL_OV518) - reg_w(ov, 0x2f, 0x80); - - return (reg_w(ov, R51x_SYS_RESET, 0x00)); - } - - return 0; -} - -/* Sleeps until no frames are active. Returns !0 if got signal */ -static int -ov51x_wait_frames_inactive(struct usb_ov511 *ov) -{ - return wait_event_interruptible(ov->wq, ov->curframe < 0); -} - -/* Resets the hardware snapshot button */ -static void -ov51x_clear_snapshot(struct usb_ov511 *ov) -{ - if (ov->bclass == BCL_OV511) { - reg_w(ov, R51x_SYS_SNAP, 0x00); - reg_w(ov, R51x_SYS_SNAP, 0x02); - reg_w(ov, R51x_SYS_SNAP, 0x00); - } else if (ov->bclass == BCL_OV518) { - dev_warn(&ov->dev->dev, - "snapshot reset not supported yet on OV518(+)\n"); - } else { - dev_err(&ov->dev->dev, "clear snap: invalid bridge type\n"); - } -} - -#if 0 -/* Checks the status of the snapshot button. Returns 1 if it was pressed since - * it was last cleared, and zero in all other cases (including errors) */ -static int -ov51x_check_snapshot(struct usb_ov511 *ov) -{ - int ret, status = 0; - - if (ov->bclass == BCL_OV511) { - ret = reg_r(ov, R51x_SYS_SNAP); - if (ret < 0) { - dev_err(&ov->dev->dev, - "Error checking snspshot status (%d)\n", ret); - } else if (ret & 0x08) { - status = 1; - } - } else if (ov->bclass == BCL_OV518) { - dev_warn(&ov->dev->dev, - "snapshot check not supported yet on OV518(+)\n"); - } else { - dev_err(&ov->dev->dev, "clear snap: invalid bridge type\n"); - } - - return status; -} -#endif - -/* This does an initial reset of an OmniVision sensor and ensures that I2C - * is synchronized. Returns <0 for failure. - */ -static int -init_ov_sensor(struct usb_ov511 *ov) -{ - int i, success; - - /* Reset the sensor */ - if (i2c_w(ov, 0x12, 0x80) < 0) - return -EIO; - - /* Wait for it to initialize */ - msleep(150); - - for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { - if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && - (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { - success = 1; - continue; - } - - /* Reset the sensor */ - if (i2c_w(ov, 0x12, 0x80) < 0) - return -EIO; - /* Wait for it to initialize */ - msleep(150); - /* Dummy read to sync I2C */ - if (i2c_r(ov, 0x00) < 0) - return -EIO; - } - - if (!success) - return -EIO; - - PDEBUG(1, "I2C synced in %d attempt(s)", i); - - return 0; -} - -static int -ov511_set_packet_size(struct usb_ov511 *ov, int size) -{ - int alt, mult; - - if (ov51x_stop(ov) < 0) - return -EIO; - - mult = size >> 5; - - if (ov->bridge == BRG_OV511) { - if (size == 0) - alt = OV511_ALT_SIZE_0; - else if (size == 257) - alt = OV511_ALT_SIZE_257; - else if (size == 513) - alt = OV511_ALT_SIZE_513; - else if (size == 769) - alt = OV511_ALT_SIZE_769; - else if (size == 993) - alt = OV511_ALT_SIZE_993; - else { - err("Set packet size: invalid size (%d)", size); - return -EINVAL; - } - } else if (ov->bridge == BRG_OV511PLUS) { - if (size == 0) - alt = OV511PLUS_ALT_SIZE_0; - else if (size == 33) - alt = OV511PLUS_ALT_SIZE_33; - else if (size == 129) - alt = OV511PLUS_ALT_SIZE_129; - else if (size == 257) - alt = OV511PLUS_ALT_SIZE_257; - else if (size == 385) - alt = OV511PLUS_ALT_SIZE_385; - else if (size == 513) - alt = OV511PLUS_ALT_SIZE_513; - else if (size == 769) - alt = OV511PLUS_ALT_SIZE_769; - else if (size == 961) - alt = OV511PLUS_ALT_SIZE_961; - else { - err("Set packet size: invalid size (%d)", size); - return -EINVAL; - } - } else { - err("Set packet size: Invalid bridge type"); - return -EINVAL; - } - - PDEBUG(3, "%d, mult=%d, alt=%d", size, mult, alt); - - if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0) - return -EIO; - - if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { - err("Set packet size: set interface error"); - return -EBUSY; - } - - if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) - return -EIO; - - ov->packet_size = size; - - if (ov51x_restart(ov) < 0) - return -EIO; - - return 0; -} - -/* Note: Unlike the OV511/OV511+, the size argument does NOT include the - * optional packet number byte. The actual size *is* stored in ov->packet_size, - * though. */ -static int -ov518_set_packet_size(struct usb_ov511 *ov, int size) -{ - int alt; - - if (ov51x_stop(ov) < 0) - return -EIO; - - if (ov->bclass == BCL_OV518) { - if (size == 0) - alt = OV518_ALT_SIZE_0; - else if (size == 128) - alt = OV518_ALT_SIZE_128; - else if (size == 256) - alt = OV518_ALT_SIZE_256; - else if (size == 384) - alt = OV518_ALT_SIZE_384; - else if (size == 512) - alt = OV518_ALT_SIZE_512; - else if (size == 640) - alt = OV518_ALT_SIZE_640; - else if (size == 768) - alt = OV518_ALT_SIZE_768; - else if (size == 896) - alt = OV518_ALT_SIZE_896; - else { - err("Set packet size: invalid size (%d)", size); - return -EINVAL; - } - } else { - err("Set packet size: Invalid bridge type"); - return -EINVAL; - } - - PDEBUG(3, "%d, alt=%d", size, alt); - - ov->packet_size = size; - if (size > 0) { - /* Program ISO FIFO size reg (packet number isn't included) */ - ov518_reg_w32(ov, 0x30, size, 2); - - if (ov->packet_numbering) - ++ov->packet_size; - } - - if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { - err("Set packet size: set interface error"); - return -EBUSY; - } - - /* Initialize the stream */ - if (reg_w(ov, 0x2f, 0x80) < 0) - return -EIO; - - if (ov51x_restart(ov) < 0) - return -EIO; - - if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) - return -EIO; - - return 0; -} - -/* Upload compression params and quantization tables. Returns 0 for success. */ -static int -ov511_init_compression(struct usb_ov511 *ov) -{ - int rc = 0; - - if (!ov->compress_inited) { - reg_w(ov, 0x70, phy); - reg_w(ov, 0x71, phuv); - reg_w(ov, 0x72, pvy); - reg_w(ov, 0x73, pvuv); - reg_w(ov, 0x74, qhy); - reg_w(ov, 0x75, qhuv); - reg_w(ov, 0x76, qvy); - reg_w(ov, 0x77, qvuv); - - if (ov511_upload_quan_tables(ov) < 0) { - err("Error uploading quantization tables"); - rc = -EIO; - goto out; - } - } - - ov->compress_inited = 1; -out: - return rc; -} - -/* Upload compression params and quantization tables. Returns 0 for success. */ -static int -ov518_init_compression(struct usb_ov511 *ov) -{ - int rc = 0; - - if (!ov->compress_inited) { - if (ov518_upload_quan_tables(ov) < 0) { - err("Error uploading quantization tables"); - rc = -EIO; - goto out; - } - } - - ov->compress_inited = 1; -out: - return rc; -} - -/* -------------------------------------------------------------------------- */ - -/* Sets sensor's contrast setting to "val" */ -static int -sensor_set_contrast(struct usb_ov511 *ov, unsigned short val) -{ - int rc; - - PDEBUG(3, "%d", val); - - if (ov->stop_during_set) - if (ov51x_stop(ov) < 0) - return -EIO; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV6620: - { - rc = i2c_w(ov, OV7610_REG_CNT, val >> 8); - if (rc < 0) - goto out; - break; - } - case SEN_OV6630: - { - rc = i2c_w_mask(ov, OV7610_REG_CNT, val >> 12, 0x0f); - if (rc < 0) - goto out; - break; - } - case SEN_OV7620: - { - unsigned char ctab[] = { - 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, - 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff - }; - - /* Use Y gamma control instead. Bit 0 enables it. */ - rc = i2c_w(ov, 0x64, ctab[val>>12]); - if (rc < 0) - goto out; - break; - } - case SEN_SAA7111A: - { - rc = i2c_w(ov, 0x0b, val >> 9); - if (rc < 0) - goto out; - break; - } - default: - { - PDEBUG(3, "Unsupported with this sensor"); - rc = -EPERM; - goto out; - } - } - - rc = 0; /* Success */ - ov->contrast = val; -out: - if (ov51x_restart(ov) < 0) - return -EIO; - - return rc; -} - -/* Gets sensor's contrast setting */ -static int -sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val) -{ - int rc; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV6620: - rc = i2c_r(ov, OV7610_REG_CNT); - if (rc < 0) - return rc; - else - *val = rc << 8; - break; - case SEN_OV6630: - rc = i2c_r(ov, OV7610_REG_CNT); - if (rc < 0) - return rc; - else - *val = rc << 12; - break; - case SEN_OV7620: - /* Use Y gamma reg instead. Bit 0 is the enable bit. */ - rc = i2c_r(ov, 0x64); - if (rc < 0) - return rc; - else - *val = (rc & 0xfe) << 8; - break; - case SEN_SAA7111A: - *val = ov->contrast; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - return -EPERM; - } - - PDEBUG(3, "%d", *val); - ov->contrast = *val; - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -/* Sets sensor's brightness setting to "val" */ -static int -sensor_set_brightness(struct usb_ov511 *ov, unsigned short val) -{ - int rc; - - PDEBUG(4, "%d", val); - - if (ov->stop_during_set) - if (ov51x_stop(ov) < 0) - return -EIO; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV76BE: - case SEN_OV6620: - case SEN_OV6630: - rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); - if (rc < 0) - goto out; - break; - case SEN_OV7620: - /* 7620 doesn't like manual changes when in auto mode */ - if (!ov->auto_brt) { - rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); - if (rc < 0) - goto out; - } - break; - case SEN_SAA7111A: - rc = i2c_w(ov, 0x0a, val >> 8); - if (rc < 0) - goto out; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - rc = -EPERM; - goto out; - } - - rc = 0; /* Success */ - ov->brightness = val; -out: - if (ov51x_restart(ov) < 0) - return -EIO; - - return rc; -} - -/* Gets sensor's brightness setting */ -static int -sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val) -{ - int rc; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV76BE: - case SEN_OV7620: - case SEN_OV6620: - case SEN_OV6630: - rc = i2c_r(ov, OV7610_REG_BRT); - if (rc < 0) - return rc; - else - *val = rc << 8; - break; - case SEN_SAA7111A: - *val = ov->brightness; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - return -EPERM; - } - - PDEBUG(3, "%d", *val); - ov->brightness = *val; - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -/* Sets sensor's saturation (color intensity) setting to "val" */ -static int -sensor_set_saturation(struct usb_ov511 *ov, unsigned short val) -{ - int rc; - - PDEBUG(3, "%d", val); - - if (ov->stop_during_set) - if (ov51x_stop(ov) < 0) - return -EIO; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV76BE: - case SEN_OV6620: - case SEN_OV6630: - rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); - if (rc < 0) - goto out; - break; - case SEN_OV7620: -// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ -// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e); -// if (rc < 0) -// goto out; - rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); - if (rc < 0) - goto out; - break; - case SEN_SAA7111A: - rc = i2c_w(ov, 0x0c, val >> 9); - if (rc < 0) - goto out; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - rc = -EPERM; - goto out; - } - - rc = 0; /* Success */ - ov->colour = val; -out: - if (ov51x_restart(ov) < 0) - return -EIO; - - return rc; -} - -/* Gets sensor's saturation (color intensity) setting */ -static int -sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val) -{ - int rc; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV76BE: - case SEN_OV6620: - case SEN_OV6630: - rc = i2c_r(ov, OV7610_REG_SAT); - if (rc < 0) - return rc; - else - *val = rc << 8; - break; - case SEN_OV7620: -// /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */ -// rc = i2c_r(ov, 0x62); -// if (rc < 0) -// return rc; -// else -// *val = (rc & 0x7e) << 9; - rc = i2c_r(ov, OV7610_REG_SAT); - if (rc < 0) - return rc; - else - *val = rc << 8; - break; - case SEN_SAA7111A: - *val = ov->colour; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - return -EPERM; - } - - PDEBUG(3, "%d", *val); - ov->colour = *val; - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -/* Sets sensor's hue (red/blue balance) setting to "val" */ -static int -sensor_set_hue(struct usb_ov511 *ov, unsigned short val) -{ - int rc; - - PDEBUG(3, "%d", val); - - if (ov->stop_during_set) - if (ov51x_stop(ov) < 0) - return -EIO; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV6620: - case SEN_OV6630: - rc = i2c_w(ov, OV7610_REG_RED, 0xFF - (val >> 8)); - if (rc < 0) - goto out; - - rc = i2c_w(ov, OV7610_REG_BLUE, val >> 8); - if (rc < 0) - goto out; - break; - case SEN_OV7620: -// Hue control is causing problems. I will enable it once it's fixed. -#if 0 - rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb); - if (rc < 0) - goto out; - - rc = i2c_w(ov, 0x79, (unsigned char)(val >> 8) + 0xb); - if (rc < 0) - goto out; -#endif - break; - case SEN_SAA7111A: - rc = i2c_w(ov, 0x0d, (val + 32768) >> 8); - if (rc < 0) - goto out; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - rc = -EPERM; - goto out; - } - - rc = 0; /* Success */ - ov->hue = val; -out: - if (ov51x_restart(ov) < 0) - return -EIO; - - return rc; -} - -/* Gets sensor's hue (red/blue balance) setting */ -static int -sensor_get_hue(struct usb_ov511 *ov, unsigned short *val) -{ - int rc; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV6620: - case SEN_OV6630: - rc = i2c_r(ov, OV7610_REG_BLUE); - if (rc < 0) - return rc; - else - *val = rc << 8; - break; - case SEN_OV7620: - rc = i2c_r(ov, 0x7a); - if (rc < 0) - return rc; - else - *val = rc << 8; - break; - case SEN_SAA7111A: - *val = ov->hue; - break; - default: - PDEBUG(3, "Unsupported with this sensor"); - return -EPERM; - } - - PDEBUG(3, "%d", *val); - ov->hue = *val; - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -static int -sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p) -{ - int rc; - - PDEBUG(4, "sensor_set_picture"); - - ov->whiteness = p->whiteness; - - /* Don't return error if a setting is unsupported, or rest of settings - * will not be performed */ - - rc = sensor_set_contrast(ov, p->contrast); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_set_brightness(ov, p->brightness); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_set_saturation(ov, p->colour); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_set_hue(ov, p->hue); - if (FATAL_ERROR(rc)) - return rc; - - return 0; -} - -static int -sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) -{ - int rc; - - PDEBUG(4, "sensor_get_picture"); - - /* Don't return error if a setting is unsupported, or rest of settings - * will not be performed */ - - rc = sensor_get_contrast(ov, &(p->contrast)); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_get_brightness(ov, &(p->brightness)); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_get_saturation(ov, &(p->colour)); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_get_hue(ov, &(p->hue)); - if (FATAL_ERROR(rc)) - return rc; - - p->whiteness = 105 << 8; - - return 0; -} - -#if 0 -// FIXME: Exposure range is only 0x00-0x7f in interlace mode -/* Sets current exposure for sensor. This only has an effect if auto-exposure - * is off */ -static inline int -sensor_set_exposure(struct usb_ov511 *ov, unsigned char val) -{ - int rc; - - PDEBUG(3, "%d", val); - - if (ov->stop_during_set) - if (ov51x_stop(ov) < 0) - return -EIO; - - switch (ov->sensor) { - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV7610: - case SEN_OV7620: - case SEN_OV76BE: - case SEN_OV8600: - rc = i2c_w(ov, 0x10, val); - if (rc < 0) - goto out; - - break; - case SEN_KS0127: - case SEN_KS0127B: - case SEN_SAA7111A: - PDEBUG(3, "Unsupported with this sensor"); - return -EPERM; - default: - err("Sensor not supported for set_exposure"); - return -EINVAL; - } - - rc = 0; /* Success */ - ov->exposure = val; -out: - if (ov51x_restart(ov) < 0) - return -EIO; - - return rc; -} -#endif - -/* Gets current exposure level from sensor, regardless of whether it is under - * manual control. */ -static int -sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) -{ - int rc; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV7620: - case SEN_OV76BE: - case SEN_OV8600: - rc = i2c_r(ov, 0x10); - if (rc < 0) - return rc; - else - *val = rc; - break; - case SEN_KS0127: - case SEN_KS0127B: - case SEN_SAA7111A: - val = NULL; - PDEBUG(3, "Unsupported with this sensor"); - return -EPERM; - default: - err("Sensor not supported for get_exposure"); - return -EINVAL; - } - - PDEBUG(3, "%d", *val); - ov->exposure = *val; - - return 0; -} - -/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */ -static void -ov51x_led_control(struct usb_ov511 *ov, int enable) -{ - PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - - if (ov->bridge == BRG_OV511PLUS) - reg_w(ov, R511_SYS_LED_CTL, enable ? 1 : 0); - else if (ov->bclass == BCL_OV518) - reg_w_mask(ov, R518_GPIO_OUT, enable ? 0x02 : 0x00, 0x02); - - return; -} - -/* Matches the sensor's internal frame rate to the lighting frequency. - * Valid frequencies are: - * 50 - 50Hz, for European and Asian lighting - * 60 - 60Hz, for American lighting - * - * Tested with: OV7610, OV7620, OV76BE, OV6620 - * Unsupported: KS0127, KS0127B, SAA7111A - * Returns: 0 for success - */ -static int -sensor_set_light_freq(struct usb_ov511 *ov, int freq) -{ - int sixty; - - PDEBUG(4, "%d Hz", freq); - - if (freq == 60) - sixty = 1; - else if (freq == 50) - sixty = 0; - else { - err("Invalid light freq (%d Hz)", freq); - return -EINVAL; - } - - switch (ov->sensor) { - case SEN_OV7610: - i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); - i2c_w(ov, 0x2b, sixty?0x00:0xac); - i2c_w_mask(ov, 0x13, 0x10, 0x10); - i2c_w_mask(ov, 0x13, 0x00, 0x10); - break; - case SEN_OV7620: - case SEN_OV76BE: - case SEN_OV8600: - i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); - i2c_w(ov, 0x2b, sixty?0x00:0xac); - i2c_w_mask(ov, 0x76, 0x01, 0x01); - break; - case SEN_OV6620: - case SEN_OV6630: - i2c_w(ov, 0x2b, sixty?0xa8:0x28); - i2c_w(ov, 0x2a, sixty?0x84:0xa4); - break; - case SEN_KS0127: - case SEN_KS0127B: - case SEN_SAA7111A: - PDEBUG(5, "Unsupported with this sensor"); - return -EPERM; - default: - err("Sensor not supported for set_light_freq"); - return -EINVAL; - } - - ov->lightfreq = freq; - - return 0; -} - -/* If enable is true, turn on the sensor's banding filter, otherwise turn it - * off. This filter tries to reduce the pattern of horizontal light/dark bands - * caused by some (usually fluorescent) lighting. The light frequency must be - * set either before or after enabling it with ov51x_set_light_freq(). - * - * Tested with: OV7610, OV7620, OV76BE, OV6620. - * Unsupported: KS0127, KS0127B, SAA7111A - * Returns: 0 for success - */ -static int -sensor_set_banding_filter(struct usb_ov511 *ov, int enable) -{ - int rc; - - PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - - if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B - || ov->sensor == SEN_SAA7111A) { - PDEBUG(5, "Unsupported with this sensor"); - return -EPERM; - } - - rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04); - if (rc < 0) - return rc; - - ov->bandfilt = enable; - - return 0; -} - -/* If enable is true, turn on the sensor's auto brightness control, otherwise - * turn it off. - * - * Unsupported: KS0127, KS0127B, SAA7111A - * Returns: 0 for success - */ -static int -sensor_set_auto_brightness(struct usb_ov511 *ov, int enable) -{ - int rc; - - PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - - if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B - || ov->sensor == SEN_SAA7111A) { - PDEBUG(5, "Unsupported with this sensor"); - return -EPERM; - } - - rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10); - if (rc < 0) - return rc; - - ov->auto_brt = enable; - - return 0; -} - -/* If enable is true, turn on the sensor's auto exposure control, otherwise - * turn it off. - * - * Unsupported: KS0127, KS0127B, SAA7111A - * Returns: 0 for success - */ -static int -sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) -{ - PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - - switch (ov->sensor) { - case SEN_OV7610: - i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80); - break; - case SEN_OV6620: - case SEN_OV7620: - case SEN_OV76BE: - case SEN_OV8600: - i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); - break; - case SEN_OV6630: - i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10); - break; - case SEN_KS0127: - case SEN_KS0127B: - case SEN_SAA7111A: - PDEBUG(5, "Unsupported with this sensor"); - return -EPERM; - default: - err("Sensor not supported for set_auto_exposure"); - return -EINVAL; - } - - ov->auto_exp = enable; - - return 0; -} - -/* Modifies the sensor's exposure algorithm to allow proper exposure of objects - * that are illuminated from behind. - * - * Tested with: OV6620, OV7620 - * Unsupported: OV7610, OV76BE, KS0127, KS0127B, SAA7111A - * Returns: 0 for success - */ -static int -sensor_set_backlight(struct usb_ov511 *ov, int enable) -{ - PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - - switch (ov->sensor) { - case SEN_OV7620: - case SEN_OV8600: - i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0); - i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); - i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); - break; - case SEN_OV6620: - i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0); - i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); - i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80); - break; - case SEN_OV6630: - i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0); - i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); - i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); - break; - case SEN_OV7610: - case SEN_OV76BE: - case SEN_KS0127: - case SEN_KS0127B: - case SEN_SAA7111A: - PDEBUG(5, "Unsupported with this sensor"); - return -EPERM; - default: - err("Sensor not supported for set_backlight"); - return -EINVAL; - } - - ov->backlight = enable; - - return 0; -} - -static int -sensor_set_mirror(struct usb_ov511 *ov, int enable) -{ - PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); - - switch (ov->sensor) { - case SEN_OV6620: - case SEN_OV6630: - case SEN_OV7610: - case SEN_OV7620: - case SEN_OV76BE: - case SEN_OV8600: - i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40); - break; - case SEN_KS0127: - case SEN_KS0127B: - case SEN_SAA7111A: - PDEBUG(5, "Unsupported with this sensor"); - return -EPERM; - default: - err("Sensor not supported for set_mirror"); - return -EINVAL; - } - - ov->mirror = enable; - - return 0; -} - -/* Returns number of bits per pixel (regardless of where they are located; - * planar or not), or zero for unsupported format. - */ -static inline int -get_depth(int palette) -{ - switch (palette) { - case VIDEO_PALETTE_GREY: return 8; - case VIDEO_PALETTE_YUV420: return 12; - case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ - default: return 0; /* Invalid format */ - } -} - -/* Bytes per frame. Used by read(). Return of 0 indicates error */ -static inline long int -get_frame_length(struct ov511_frame *frame) -{ - if (!frame) - return 0; - else - return ((frame->width * frame->height - * get_depth(frame->format)) >> 3); -} - -static int -mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, - int mode, int sub_flag, int qvga) -{ - int clock; - - /******** Mode (VGA/QVGA) and sensor specific regs ********/ - - switch (ov->sensor) { - case SEN_OV7610: - i2c_w(ov, 0x14, qvga?0x24:0x04); -// FIXME: Does this improve the image quality or frame rate? -#if 0 - i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); - i2c_w(ov, 0x24, 0x10); - i2c_w(ov, 0x25, qvga?0x40:0x8a); - i2c_w(ov, 0x2f, qvga?0x30:0xb0); - i2c_w(ov, 0x35, qvga?0x1c:0x9c); -#endif - break; - case SEN_OV7620: -// i2c_w(ov, 0x2b, 0x00); - i2c_w(ov, 0x14, qvga?0xa4:0x84); - i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); - i2c_w(ov, 0x24, qvga?0x20:0x3a); - i2c_w(ov, 0x25, qvga?0x30:0x60); - i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); - i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); - i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); - break; - case SEN_OV76BE: -// i2c_w(ov, 0x2b, 0x00); - i2c_w(ov, 0x14, qvga?0xa4:0x84); -// FIXME: Enable this once 7620AE uses 7620 initial settings -#if 0 - i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); - i2c_w(ov, 0x24, qvga?0x20:0x3a); - i2c_w(ov, 0x25, qvga?0x30:0x60); - i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); - i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0); - i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); -#endif - break; - case SEN_OV6620: - i2c_w(ov, 0x14, qvga?0x24:0x04); - break; - case SEN_OV6630: - i2c_w(ov, 0x14, qvga?0xa0:0x80); - break; - default: - err("Invalid sensor"); - return -EINVAL; - } - - /******** Palette-specific regs ********/ - - if (mode == VIDEO_PALETTE_GREY) { - if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { - /* these aren't valid on the OV6620/OV7620/6630? */ - i2c_w_mask(ov, 0x0e, 0x40, 0x40); - } - - if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518 - && ov518_color) { - i2c_w_mask(ov, 0x12, 0x00, 0x10); - i2c_w_mask(ov, 0x13, 0x00, 0x20); - } else { - i2c_w_mask(ov, 0x13, 0x20, 0x20); - } - } else { - if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { - /* not valid on the OV6620/OV7620/6630? */ - i2c_w_mask(ov, 0x0e, 0x00, 0x40); - } - - /* The OV518 needs special treatment. Although both the OV518 - * and the OV6630 support a 16-bit video bus, only the 8 bit Y - * bus is actually used. The UV bus is tied to ground. - * Therefore, the OV6630 needs to be in 8-bit multiplexed - * output mode */ - - if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518 - && ov518_color) { - i2c_w_mask(ov, 0x12, 0x10, 0x10); - i2c_w_mask(ov, 0x13, 0x20, 0x20); - } else { - i2c_w_mask(ov, 0x13, 0x00, 0x20); - } - } - - /******** Clock programming ********/ - - /* The OV6620 needs special handling. This prevents the - * severe banding that normally occurs */ - if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) - { - /* Clock down */ - - i2c_w(ov, 0x2a, 0x04); - - if (ov->compress) { -// clock = 0; /* This ensures the highest frame rate */ - clock = 3; - } else if (clockdiv == -1) { /* If user didn't override it */ - clock = 3; /* Gives better exposure time */ - } else { - clock = clockdiv; - } - - PDEBUG(4, "Setting clock divisor to %d", clock); - - i2c_w(ov, 0x11, clock); - - i2c_w(ov, 0x2a, 0x84); - /* This next setting is critical. It seems to improve - * the gain or the contrast. The "reserved" bits seem - * to have some effect in this case. */ - i2c_w(ov, 0x2d, 0x85); - } - else - { - if (ov->compress) { - clock = 1; /* This ensures the highest frame rate */ - } else if (clockdiv == -1) { /* If user didn't override it */ - /* Calculate and set the clock divisor */ - clock = ((sub_flag ? ov->subw * ov->subh - : width * height) - * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2) - / 66000; - } else { - clock = clockdiv; - } - - PDEBUG(4, "Setting clock divisor to %d", clock); - - i2c_w(ov, 0x11, clock); - } - - /******** Special Features ********/ - - if (framedrop >= 0) - i2c_w(ov, 0x16, framedrop); - - /* Test Pattern */ - i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02); - - /* Enable auto white balance */ - i2c_w_mask(ov, 0x12, 0x04, 0x04); - - // This will go away as soon as ov51x_mode_init_sensor_regs() - // is fully tested. - /* 7620/6620/6630? don't have register 0x35, so play it safe */ - if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { - if (width == 640 && height == 480) - i2c_w(ov, 0x35, 0x9e); - else - i2c_w(ov, 0x35, 0x1e); - } - - return 0; -} - -static int -set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, - int sub_flag) -{ - int ret; - int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; - int hoffset, voffset, hwscale = 0, vwscale = 0; - - /* The different sensor ICs handle setting up of window differently. - * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */ - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV76BE: - hwsbase = 0x38; - hwebase = 0x3a; - vwsbase = vwebase = 0x05; - break; - case SEN_OV6620: - case SEN_OV6630: - hwsbase = 0x38; - hwebase = 0x3a; - vwsbase = 0x05; - vwebase = 0x06; - break; - case SEN_OV7620: - hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ - hwebase = 0x2f; - vwsbase = vwebase = 0x05; - break; - default: - err("Invalid sensor"); - return -EINVAL; - } - - if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { - /* Note: OV518(+) does downsample on its own) */ - if ((width > 176 && height > 144) - || ov->bclass == BCL_OV518) { /* CIF */ - ret = mode_init_ov_sensor_regs(ov, width, height, - mode, sub_flag, 0); - if (ret < 0) - return ret; - hwscale = 1; - vwscale = 1; /* The datasheet says 0; it's wrong */ - hwsize = 352; - vwsize = 288; - } else if (width > 176 || height > 144) { - err("Illegal dimensions"); - return -EINVAL; - } else { /* QCIF */ - ret = mode_init_ov_sensor_regs(ov, width, height, - mode, sub_flag, 1); - if (ret < 0) - return ret; - hwsize = 176; - vwsize = 144; - } - } else { - if (width > 320 && height > 240) { /* VGA */ - ret = mode_init_ov_sensor_regs(ov, width, height, - mode, sub_flag, 0); - if (ret < 0) - return ret; - hwscale = 2; - vwscale = 1; - hwsize = 640; - vwsize = 480; - } else if (width > 320 || height > 240) { - err("Illegal dimensions"); - return -EINVAL; - } else { /* QVGA */ - ret = mode_init_ov_sensor_regs(ov, width, height, - mode, sub_flag, 1); - if (ret < 0) - return ret; - hwscale = 1; - hwsize = 320; - vwsize = 240; - } - } - - /* Center the window */ - hoffset = ((hwsize - width) / 2) >> hwscale; - voffset = ((vwsize - height) / 2) >> vwscale; - - /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ - if (sub_flag) { - i2c_w(ov, 0x17, hwsbase+(ov->subx>>hwscale)); - i2c_w(ov, 0x18, hwebase+((ov->subx+ov->subw)>>hwscale)); - i2c_w(ov, 0x19, vwsbase+(ov->suby>>vwscale)); - i2c_w(ov, 0x1a, vwebase+((ov->suby+ov->subh)>>vwscale)); - } else { - i2c_w(ov, 0x17, hwsbase + hoffset); - i2c_w(ov, 0x18, hwebase + hoffset + (hwsize>>hwscale)); - i2c_w(ov, 0x19, vwsbase + voffset); - i2c_w(ov, 0x1a, vwebase + voffset + (vwsize>>vwscale)); - } - -#ifdef OV511_DEBUG - if (dump_sensor) - dump_i2c_regs(ov); -#endif - - return 0; -} - -/* Set up the OV511/OV511+ with the given image parameters. - * - * Do not put any sensor-specific code in here (including I2C I/O functions) - */ -static int -ov511_mode_init_regs(struct usb_ov511 *ov, - int width, int height, int mode, int sub_flag) -{ - int hsegs, vsegs; - - if (sub_flag) { - width = ov->subw; - height = ov->subh; - } - - PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", - width, height, mode, sub_flag); - - // FIXME: This should be moved to a 7111a-specific function once - // subcapture is dealt with properly - if (ov->sensor == SEN_SAA7111A) { - if (width == 320 && height == 240) { - /* No need to do anything special */ - } else if (width == 640 && height == 480) { - /* Set the OV511 up as 320x480, but keep the - * V4L resolution as 640x480 */ - width = 320; - } else { - err("SAA7111A only allows 320x240 or 640x480"); - return -EINVAL; - } - } - - /* Make sure width and height are a multiple of 8 */ - if (width % 8 || height % 8) { - err("Invalid size (%d, %d) (mode = %d)", width, height, mode); - return -EINVAL; - } - - if (width < ov->minwidth || height < ov->minheight) { - err("Requested dimensions are too small"); - return -EINVAL; - } - - if (ov51x_stop(ov) < 0) - return -EIO; - - if (mode == VIDEO_PALETTE_GREY) { - reg_w(ov, R511_CAM_UV_EN, 0x00); - reg_w(ov, R511_SNAP_UV_EN, 0x00); - reg_w(ov, R511_SNAP_OPTS, 0x01); - } else { - reg_w(ov, R511_CAM_UV_EN, 0x01); - reg_w(ov, R511_SNAP_UV_EN, 0x01); - reg_w(ov, R511_SNAP_OPTS, 0x03); - } - - /* Here I'm assuming that snapshot size == image size. - * I hope that's always true. --claudio - */ - hsegs = (width >> 3) - 1; - vsegs = (height >> 3) - 1; - - reg_w(ov, R511_CAM_PXCNT, hsegs); - reg_w(ov, R511_CAM_LNCNT, vsegs); - reg_w(ov, R511_CAM_PXDIV, 0x00); - reg_w(ov, R511_CAM_LNDIV, 0x00); - - /* YUV420, low pass filter on */ - reg_w(ov, R511_CAM_OPTS, 0x03); - - /* Snapshot additions */ - reg_w(ov, R511_SNAP_PXCNT, hsegs); - reg_w(ov, R511_SNAP_LNCNT, vsegs); - reg_w(ov, R511_SNAP_PXDIV, 0x00); - reg_w(ov, R511_SNAP_LNDIV, 0x00); - - if (ov->compress) { - /* Enable Y and UV quantization and compression */ - reg_w(ov, R511_COMP_EN, 0x07); - reg_w(ov, R511_COMP_LUT_EN, 0x03); - ov51x_reset(ov, OV511_RESET_OMNICE); - } - - if (ov51x_restart(ov) < 0) - return -EIO; - - return 0; -} - -/* Sets up the OV518/OV518+ with the given image parameters - * - * OV518 needs a completely different approach, until we can figure out what - * the individual registers do. Also, only 15 FPS is supported now. - * - * Do not put any sensor-specific code in here (including I2C I/O functions) - */ -static int -ov518_mode_init_regs(struct usb_ov511 *ov, - int width, int height, int mode, int sub_flag) -{ - int hsegs, vsegs, hi_res; - - if (sub_flag) { - width = ov->subw; - height = ov->subh; - } - - PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", - width, height, mode, sub_flag); - - if (width % 16 || height % 8) { - err("Invalid size (%d, %d)", width, height); - return -EINVAL; - } - - if (width < ov->minwidth || height < ov->minheight) { - err("Requested dimensions are too small"); - return -EINVAL; - } - - if (width >= 320 && height >= 240) { - hi_res = 1; - } else if (width >= 320 || height >= 240) { - err("Invalid width/height combination (%d, %d)", width, height); - return -EINVAL; - } else { - hi_res = 0; - } - - if (ov51x_stop(ov) < 0) - return -EIO; - - /******** Set the mode ********/ - - reg_w(ov, 0x2b, 0); - reg_w(ov, 0x2c, 0); - reg_w(ov, 0x2d, 0); - reg_w(ov, 0x2e, 0); - reg_w(ov, 0x3b, 0); - reg_w(ov, 0x3c, 0); - reg_w(ov, 0x3d, 0); - reg_w(ov, 0x3e, 0); - - if (ov->bridge == BRG_OV518 && ov518_color) { - /* OV518 needs U and V swapped */ - i2c_w_mask(ov, 0x15, 0x00, 0x01); - - if (mode == VIDEO_PALETTE_GREY) { - /* Set 16-bit input format (UV data are ignored) */ - reg_w_mask(ov, 0x20, 0x00, 0x08); - - /* Set 8-bit (4:0:0) output format */ - reg_w_mask(ov, 0x28, 0x00, 0xf0); - reg_w_mask(ov, 0x38, 0x00, 0xf0); - } else { - /* Set 8-bit (YVYU) input format */ - reg_w_mask(ov, 0x20, 0x08, 0x08); - - /* Set 12-bit (4:2:0) output format */ - reg_w_mask(ov, 0x28, 0x80, 0xf0); - reg_w_mask(ov, 0x38, 0x80, 0xf0); - } - } else { - reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); - reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); - } - - hsegs = width / 16; - vsegs = height / 4; - - reg_w(ov, 0x29, hsegs); - reg_w(ov, 0x2a, vsegs); - - reg_w(ov, 0x39, hsegs); - reg_w(ov, 0x3a, vsegs); - - /* Windows driver does this here; who knows why */ - reg_w(ov, 0x2f, 0x80); - - /******** Set the framerate (to 15 FPS) ********/ - - /* Mode independent, but framerate dependent, regs */ - reg_w(ov, 0x51, 0x02); /* Clock divider; lower==faster */ - reg_w(ov, 0x22, 0x18); - reg_w(ov, 0x23, 0xff); - - if (ov->bridge == BRG_OV518PLUS) - reg_w(ov, 0x21, 0x19); - else - reg_w(ov, 0x71, 0x19); /* Compression-related? */ - - // FIXME: Sensor-specific - /* Bit 5 is what matters here. Of course, it is "reserved" */ - i2c_w(ov, 0x54, 0x23); - - reg_w(ov, 0x2f, 0x80); - - if (ov->bridge == BRG_OV518PLUS) { - reg_w(ov, 0x24, 0x94); - reg_w(ov, 0x25, 0x90); - ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ - ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */ - ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */ - ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */ - ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ - ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ - ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */ - ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ - ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ - } else { - reg_w(ov, 0x24, 0x9f); - reg_w(ov, 0x25, 0x90); - ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ - ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */ - ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */ - ov518_reg_w32(ov, 0xc8, 142, 2); /* 8eh */ - ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ - ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ - ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */ - ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ - ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ - } - - reg_w(ov, 0x2f, 0x80); - - if (ov51x_restart(ov) < 0) - return -EIO; - - /* Reset it just for good measure */ - if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) - return -EIO; - - return 0; -} - -/* This is a wrapper around the OV511, OV518, and sensor specific functions */ -static int -mode_init_regs(struct usb_ov511 *ov, - int width, int height, int mode, int sub_flag) -{ - int rc = 0; - - if (!ov || !ov->dev) - return -EFAULT; - - if (ov->bclass == BCL_OV518) { - rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag); - } else { - rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag); - } - - if (FATAL_ERROR(rc)) - return rc; - - switch (ov->sensor) { - case SEN_OV7610: - case SEN_OV7620: - case SEN_OV76BE: - case SEN_OV8600: - case SEN_OV6620: - case SEN_OV6630: - rc = set_ov_sensor_window(ov, width, height, mode, sub_flag); - break; - case SEN_KS0127: - case SEN_KS0127B: - err("KS0127-series decoders not supported yet"); - rc = -EINVAL; - break; - case SEN_SAA7111A: -// rc = mode_init_saa_sensor_regs(ov, width, height, mode, -// sub_flag); - - PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f)); - break; - default: - err("Unknown sensor"); - rc = -EINVAL; - } - - if (FATAL_ERROR(rc)) - return rc; - - /* Sensor-independent settings */ - rc = sensor_set_auto_brightness(ov, ov->auto_brt); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_set_auto_exposure(ov, ov->auto_exp); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_set_banding_filter(ov, bandingfilter); - if (FATAL_ERROR(rc)) - return rc; - - if (ov->lightfreq) { - rc = sensor_set_light_freq(ov, lightfreq); - if (FATAL_ERROR(rc)) - return rc; - } - - rc = sensor_set_backlight(ov, ov->backlight); - if (FATAL_ERROR(rc)) - return rc; - - rc = sensor_set_mirror(ov, ov->mirror); - if (FATAL_ERROR(rc)) - return rc; - - return 0; -} - -/* This sets the default image parameters. This is useful for apps that use - * read() and do not set these. - */ -static int -ov51x_set_default_params(struct usb_ov511 *ov) -{ - int i; - - /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used - * (using read() instead). */ - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].width = ov->maxwidth; - ov->frame[i].height = ov->maxheight; - ov->frame[i].bytes_read = 0; - if (force_palette) - ov->frame[i].format = force_palette; - else - ov->frame[i].format = VIDEO_PALETTE_YUV420; - - ov->frame[i].depth = get_depth(ov->frame[i].format); - } - - PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight, - symbolic(v4l1_plist, ov->frame[0].format)); - - /* Initialize to max width/height, YUV420 or RGB24 (if supported) */ - if (mode_init_regs(ov, ov->maxwidth, ov->maxheight, - ov->frame[0].format, 0) < 0) - return -EINVAL; - - return 0; -} - -/********************************************************************** - * - * Video decoder stuff - * - **********************************************************************/ - -/* Set analog input port of decoder */ -static int -decoder_set_input(struct usb_ov511 *ov, int input) -{ - PDEBUG(4, "port %d", input); - - switch (ov->sensor) { - case SEN_SAA7111A: - { - /* Select mode */ - i2c_w_mask(ov, 0x02, input, 0x07); - /* Bypass chrominance trap for modes 4..7 */ - i2c_w_mask(ov, 0x09, (input > 3) ? 0x80:0x00, 0x80); - break; - } - default: - return -EINVAL; - } - - return 0; -} - -/* Get ASCII name of video input */ -static int -decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) -{ - switch (ov->sensor) { - case SEN_SAA7111A: - { - if (input < 0 || input > 7) - return -EINVAL; - else if (input < 4) - sprintf(name, "CVBS-%d", input); - else // if (input < 8) - sprintf(name, "S-Video-%d", input - 4); - break; - } - default: - sprintf(name, "%s", "Camera"); - } - - return 0; -} - -/* Set norm (NTSC, PAL, SECAM, AUTO) */ -static int -decoder_set_norm(struct usb_ov511 *ov, int norm) -{ - PDEBUG(4, "%d", norm); - - switch (ov->sensor) { - case SEN_SAA7111A: - { - int reg_8, reg_e; - - if (norm == VIDEO_MODE_NTSC) { - reg_8 = 0x40; /* 60 Hz */ - reg_e = 0x00; /* NTSC M / PAL BGHI */ - } else if (norm == VIDEO_MODE_PAL) { - reg_8 = 0x00; /* 50 Hz */ - reg_e = 0x00; /* NTSC M / PAL BGHI */ - } else if (norm == VIDEO_MODE_AUTO) { - reg_8 = 0x80; /* Auto field detect */ - reg_e = 0x00; /* NTSC M / PAL BGHI */ - } else if (norm == VIDEO_MODE_SECAM) { - reg_8 = 0x00; /* 50 Hz */ - reg_e = 0x50; /* SECAM / PAL 4.43 */ - } else { - return -EINVAL; - } - - i2c_w_mask(ov, 0x08, reg_8, 0xc0); - i2c_w_mask(ov, 0x0e, reg_e, 0x70); - break; - } - default: - return -EINVAL; - } - - return 0; -} - -/********************************************************************** - * - * Raw data parsing - * - **********************************************************************/ - -/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the - * image at pOut is specified by w. - */ -static inline void -make_8x8(unsigned char *pIn, unsigned char *pOut, int w) -{ - unsigned char *pOut1 = pOut; - int x, y; - - for (y = 0; y < 8; y++) { - pOut1 = pOut; - for (x = 0; x < 8; x++) { - *pOut1++ = *pIn++; - } - pOut += w; - } -} - -/* - * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments. - * The segments represent 4 squares of 8x8 pixels as follows: - * - * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 - * 8 9 ... 15 72 73 ... 79 200 201 ... 207 - * ... ... ... - * 56 57 ... 63 120 121 ... 127 248 249 ... 255 - * - */ -static void -yuv400raw_to_yuv400p(struct ov511_frame *frame, - unsigned char *pIn0, unsigned char *pOut0) -{ - int x, y; - unsigned char *pIn, *pOut, *pOutLine; - - /* Copy Y */ - pIn = pIn0; - pOutLine = pOut0; - for (y = 0; y < frame->rawheight - 1; y += 8) { - pOut = pOutLine; - for (x = 0; x < frame->rawwidth - 1; x += 8) { - make_8x8(pIn, pOut, frame->rawwidth); - pIn += 64; - pOut += 8; - } - pOutLine += 8 * frame->rawwidth; - } -} - -/* - * For YUV 4:2:0 images, the data show up in 384 byte segments. - * The first 64 bytes of each segment are U, the next 64 are V. The U and - * V are arranged as follows: - * - * 0 1 ... 7 - * 8 9 ... 15 - * ... - * 56 57 ... 63 - * - * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). - * - * The next 256 bytes are full resolution Y data and represent 4 squares - * of 8x8 pixels as follows: - * - * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 - * 8 9 ... 15 72 73 ... 79 200 201 ... 207 - * ... ... ... - * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 - * - * Note that the U and V data in one segment represent a 16 x 16 pixel - * area, but the Y data represent a 32 x 8 pixel area. If the width is not an - * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the - * next horizontal stripe. - * - * If dumppix module param is set, _parse_data just dumps the incoming segments, - * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 - * this puts the data on the standard output and can be analyzed with the - * parseppm.c utility I wrote. That's a much faster way for figuring out how - * these data are scrambled. - */ - -/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0. - * - * FIXME: Currently only handles width and height that are multiples of 16 - */ -static void -yuv420raw_to_yuv420p(struct ov511_frame *frame, - unsigned char *pIn0, unsigned char *pOut0) -{ - int k, x, y; - unsigned char *pIn, *pOut, *pOutLine; - const unsigned int a = frame->rawwidth * frame->rawheight; - const unsigned int w = frame->rawwidth / 2; - - /* Copy U and V */ - pIn = pIn0; - pOutLine = pOut0 + a; - for (y = 0; y < frame->rawheight - 1; y += 16) { - pOut = pOutLine; - for (x = 0; x < frame->rawwidth - 1; x += 16) { - make_8x8(pIn, pOut, w); - make_8x8(pIn + 64, pOut + a/4, w); - pIn += 384; - pOut += 8; - } - pOutLine += 8 * w; - } - - /* Copy Y */ - pIn = pIn0 + 128; - pOutLine = pOut0; - k = 0; - for (y = 0; y < frame->rawheight - 1; y += 8) { - pOut = pOutLine; - for (x = 0; x < frame->rawwidth - 1; x += 8) { - make_8x8(pIn, pOut, frame->rawwidth); - pIn += 64; - pOut += 8; - if ((++k) > 3) { - k = 0; - pIn += 128; - } - } - pOutLine += 8 * frame->rawwidth; - } -} - -/********************************************************************** - * - * Decompression - * - **********************************************************************/ - -static int -request_decompressor(struct usb_ov511 *ov) -{ - if (ov->bclass == BCL_OV511 || ov->bclass == BCL_OV518) { - err("No decompressor available"); - } else { - err("Unknown bridge"); - } - - return -ENOSYS; -} - -static void -decompress(struct usb_ov511 *ov, struct ov511_frame *frame, - unsigned char *pIn0, unsigned char *pOut0) -{ - if (!ov->decomp_ops) - if (request_decompressor(ov)) - return; - -} - -/********************************************************************** - * - * Format conversion - * - **********************************************************************/ - -/* Fuses even and odd fields together, and doubles width. - * INPUT: an odd field followed by an even field at pIn0, in YUV planar format - * OUTPUT: a normal YUV planar image, with correct aspect ratio - */ -static void -deinterlace(struct ov511_frame *frame, int rawformat, - unsigned char *pIn0, unsigned char *pOut0) -{ - const int fieldheight = frame->rawheight / 2; - const int fieldpix = fieldheight * frame->rawwidth; - const int w = frame->width; - int x, y; - unsigned char *pInEven, *pInOdd, *pOut; - - PDEBUG(5, "fieldheight=%d", fieldheight); - - if (frame->rawheight != frame->height) { - err("invalid height"); - return; - } - - if ((frame->rawwidth * 2) != frame->width) { - err("invalid width"); - return; - } - - /* Y */ - pInOdd = pIn0; - pInEven = pInOdd + fieldpix; - pOut = pOut0; - for (y = 0; y < fieldheight; y++) { - for (x = 0; x < frame->rawwidth; x++) { - *pOut = *pInEven; - *(pOut+1) = *pInEven++; - *(pOut+w) = *pInOdd; - *(pOut+w+1) = *pInOdd++; - pOut += 2; - } - pOut += w; - } - - if (rawformat == RAWFMT_YUV420) { - /* U */ - pInOdd = pIn0 + fieldpix * 2; - pInEven = pInOdd + fieldpix / 4; - for (y = 0; y < fieldheight / 2; y++) { - for (x = 0; x < frame->rawwidth / 2; x++) { - *pOut = *pInEven; - *(pOut+1) = *pInEven++; - *(pOut+w/2) = *pInOdd; - *(pOut+w/2+1) = *pInOdd++; - pOut += 2; - } - pOut += w/2; - } - /* V */ - pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2; - pInEven = pInOdd + fieldpix / 4; - for (y = 0; y < fieldheight / 2; y++) { - for (x = 0; x < frame->rawwidth / 2; x++) { - *pOut = *pInEven; - *(pOut+1) = *pInEven++; - *(pOut+w/2) = *pInOdd; - *(pOut+w/2+1) = *pInOdd++; - pOut += 2; - } - pOut += w/2; - } - } -} - -static void -ov51x_postprocess_grey(struct usb_ov511 *ov, struct ov511_frame *frame) -{ - /* Deinterlace frame, if necessary */ - if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { - if (frame->compressed) - decompress(ov, frame, frame->rawdata, - frame->tempdata); - else - yuv400raw_to_yuv400p(frame, frame->rawdata, - frame->tempdata); - - deinterlace(frame, RAWFMT_YUV400, frame->tempdata, - frame->data); - } else { - if (frame->compressed) - decompress(ov, frame, frame->rawdata, - frame->data); - else - yuv400raw_to_yuv400p(frame, frame->rawdata, - frame->data); - } -} - -/* Process raw YUV420 data into standard YUV420P */ -static void -ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame) -{ - /* Deinterlace frame, if necessary */ - if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { - if (frame->compressed) - decompress(ov, frame, frame->rawdata, frame->tempdata); - else - yuv420raw_to_yuv420p(frame, frame->rawdata, - frame->tempdata); - - deinterlace(frame, RAWFMT_YUV420, frame->tempdata, - frame->data); - } else { - if (frame->compressed) - decompress(ov, frame, frame->rawdata, frame->data); - else - yuv420raw_to_yuv420p(frame, frame->rawdata, - frame->data); - } -} - -/* Post-processes the specified frame. This consists of: - * 1. Decompress frame, if necessary - * 2. Deinterlace frame and scale to proper size, if necessary - * 3. Convert from YUV planar to destination format, if necessary - * 4. Fix the RGB offset, if necessary - */ -static void -ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) -{ - if (dumppix) { - memset(frame->data, 0, - MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); - PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd); - memcpy(frame->data, frame->rawdata, frame->bytes_recvd); - } else { - switch (frame->format) { - case VIDEO_PALETTE_GREY: - ov51x_postprocess_grey(ov, frame); - break; - case VIDEO_PALETTE_YUV420: - case VIDEO_PALETTE_YUV420P: - ov51x_postprocess_yuv420(ov, frame); - break; - default: - err("Cannot convert data to %s", - symbolic(v4l1_plist, frame->format)); - } - } -} - -/********************************************************************** - * - * OV51x data transfer, IRQ handler - * - **********************************************************************/ - -static inline void -ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n) -{ - int num, offset; - int pnum = in[ov->packet_size - 1]; /* Get packet number */ - int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); - struct ov511_frame *frame = &ov->frame[ov->curframe]; - struct timeval *ts; - - /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th - * byte non-zero. The EOF packet has image width/height in the - * 10th and 11th bytes. The 9th byte is given as follows: - * - * bit 7: EOF - * 6: compression enabled - * 5: 422/420/400 modes - * 4: 422/420/400 modes - * 3: 1 - * 2: snapshot button on - * 1: snapshot frame - * 0: even/odd field - */ - - if (printph) { - dev_info(&ov->dev->dev, - "ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n", - pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6], - in[7], in[8], in[9], in[10], in[11]); - } - - /* Check for SOF/EOF packet */ - if ((in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) || - (~in[8] & 0x08)) - goto check_middle; - - /* Frame end */ - if (in[8] & 0x80) { - ts = (struct timeval *)(frame->data - + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); - do_gettimeofday(ts); - - /* Get the actual frame size from the EOF header */ - frame->rawwidth = ((int)(in[9]) + 1) * 8; - frame->rawheight = ((int)(in[10]) + 1) * 8; - - PDEBUG(4, "Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d", - ov->curframe, pnum, frame->rawwidth, frame->rawheight, - frame->bytes_recvd); - - /* Validate the header data */ - RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); - RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, - ov->maxheight); - - /* Don't allow byte count to exceed buffer size */ - RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); - - if (frame->scanstate == STATE_LINES) { - int nextf; - - frame->grabstate = FRAME_DONE; - wake_up_interruptible(&frame->wq); - - /* If next frame is ready or grabbing, - * point to it */ - nextf = (ov->curframe + 1) % OV511_NUMFRAMES; - if (ov->frame[nextf].grabstate == FRAME_READY - || ov->frame[nextf].grabstate == FRAME_GRABBING) { - ov->curframe = nextf; - ov->frame[nextf].scanstate = STATE_SCANNING; - } else { - if (frame->grabstate == FRAME_DONE) { - PDEBUG(4, "** Frame done **"); - } else { - PDEBUG(4, "Frame not ready? state = %d", - ov->frame[nextf].grabstate); - } - - ov->curframe = -1; - } - } else { - PDEBUG(5, "Frame done, but not scanning"); - } - /* Image corruption caused by misplaced frame->segment = 0 - * fixed by carlosf@conectiva.com.br - */ - } else { - /* Frame start */ - PDEBUG(4, "Frame start, framenum = %d", ov->curframe); - - /* Check to see if it's a snapshot frame */ - /* FIXME?? Should the snapshot reset go here? Performance? */ - if (in[8] & 0x02) { - frame->snapshot = 1; - PDEBUG(3, "snapshot detected"); - } - - frame->scanstate = STATE_LINES; - frame->bytes_recvd = 0; - frame->compressed = in[8] & 0x40; - } - -check_middle: - /* Are we in a frame? */ - if (frame->scanstate != STATE_LINES) { - PDEBUG(5, "Not in a frame; packet skipped"); - return; - } - - /* If frame start, skip header */ - if (frame->bytes_recvd == 0) - offset = 9; - else - offset = 0; - - num = n - offset - 1; - - /* Dump all data exactly as received */ - if (dumppix == 2) { - frame->bytes_recvd += n - 1; - if (frame->bytes_recvd <= max_raw) - memcpy(frame->rawdata + frame->bytes_recvd - (n - 1), - in, n - 1); - else - PDEBUG(3, "Raw data buffer overrun!! (%d)", - frame->bytes_recvd - max_raw); - } else if (!frame->compressed && !remove_zeros) { - frame->bytes_recvd += num; - if (frame->bytes_recvd <= max_raw) - memcpy(frame->rawdata + frame->bytes_recvd - num, - in + offset, num); - else - PDEBUG(3, "Raw data buffer overrun!! (%d)", - frame->bytes_recvd - max_raw); - } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */ - int b, read = 0, allzero, copied = 0; - if (offset) { - frame->bytes_recvd += 32 - offset; // Bytes out - memcpy(frame->rawdata, in + offset, 32 - offset); - read += 32; - } - - while (read < n - 1) { - allzero = 1; - for (b = 0; b < 32; b++) { - if (in[read + b]) { - allzero = 0; - break; - } - } - - if (allzero) { - /* Don't copy it */ - } else { - if (frame->bytes_recvd + copied + 32 <= max_raw) - { - memcpy(frame->rawdata - + frame->bytes_recvd + copied, - in + read, 32); - copied += 32; - } else { - PDEBUG(3, "Raw data buffer overrun!!"); - } - } - read += 32; - } - - frame->bytes_recvd += copied; - } -} - -static inline void -ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) -{ - int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); - struct ov511_frame *frame = &ov->frame[ov->curframe]; - struct timeval *ts; - - /* Don't copy the packet number byte */ - if (ov->packet_numbering) - --n; - - /* A false positive here is likely, until OVT gives me - * the definitive SOF/EOF format */ - if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) { - if (printph) { - dev_info(&ov->dev->dev, - "ph: %2x %2x %2x %2x %2x %2x %2x %2x\n", - in[0], in[1], in[2], in[3], in[4], in[5], - in[6], in[7]); - } - - if (frame->scanstate == STATE_LINES) { - PDEBUG(4, "Detected frame end/start"); - goto eof; - } else { //scanstate == STATE_SCANNING - /* Frame start */ - PDEBUG(4, "Frame start, framenum = %d", ov->curframe); - goto sof; - } - } else { - goto check_middle; - } - -eof: - ts = (struct timeval *)(frame->data - + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); - do_gettimeofday(ts); - - PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d", - ov->curframe, - (int)(in[9]), (int)(in[10]), frame->bytes_recvd); - - // FIXME: Since we don't know the header formats yet, - // there is no way to know what the actual image size is - frame->rawwidth = frame->width; - frame->rawheight = frame->height; - - /* Validate the header data */ - RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); - RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight); - - /* Don't allow byte count to exceed buffer size */ - RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); - - if (frame->scanstate == STATE_LINES) { - int nextf; - - frame->grabstate = FRAME_DONE; - wake_up_interruptible(&frame->wq); - - /* If next frame is ready or grabbing, - * point to it */ - nextf = (ov->curframe + 1) % OV511_NUMFRAMES; - if (ov->frame[nextf].grabstate == FRAME_READY - || ov->frame[nextf].grabstate == FRAME_GRABBING) { - ov->curframe = nextf; - ov->frame[nextf].scanstate = STATE_SCANNING; - frame = &ov->frame[nextf]; - } else { - if (frame->grabstate == FRAME_DONE) { - PDEBUG(4, "** Frame done **"); - } else { - PDEBUG(4, "Frame not ready? state = %d", - ov->frame[nextf].grabstate); - } - - ov->curframe = -1; - PDEBUG(4, "SOF dropped (no active frame)"); - return; /* Nowhere to store this frame */ - } - } -sof: - PDEBUG(4, "Starting capture on frame %d", frame->framenum); - -// Snapshot not reverse-engineered yet. -#if 0 - /* Check to see if it's a snapshot frame */ - /* FIXME?? Should the snapshot reset go here? Performance? */ - if (in[8] & 0x02) { - frame->snapshot = 1; - PDEBUG(3, "snapshot detected"); - } -#endif - frame->scanstate = STATE_LINES; - frame->bytes_recvd = 0; - frame->compressed = 1; - -check_middle: - /* Are we in a frame? */ - if (frame->scanstate != STATE_LINES) { - PDEBUG(4, "scanstate: no SOF yet"); - return; - } - - /* Dump all data exactly as received */ - if (dumppix == 2) { - frame->bytes_recvd += n; - if (frame->bytes_recvd <= max_raw) - memcpy(frame->rawdata + frame->bytes_recvd - n, in, n); - else - PDEBUG(3, "Raw data buffer overrun!! (%d)", - frame->bytes_recvd - max_raw); - } else { - /* All incoming data are divided into 8-byte segments. If the - * segment contains all zero bytes, it must be skipped. These - * zero-segments allow the OV518 to mainain a constant data rate - * regardless of the effectiveness of the compression. Segments - * are aligned relative to the beginning of each isochronous - * packet. The first segment in each image is a header (the - * decompressor skips it later). - */ - - int b, read = 0, allzero, copied = 0; - - while (read < n) { - allzero = 1; - for (b = 0; b < 8; b++) { - if (in[read + b]) { - allzero = 0; - break; - } - } - - if (allzero) { - /* Don't copy it */ - } else { - if (frame->bytes_recvd + copied + 8 <= max_raw) - { - memcpy(frame->rawdata - + frame->bytes_recvd + copied, - in + read, 8); - copied += 8; - } else { - PDEBUG(3, "Raw data buffer overrun!!"); - } - } - read += 8; - } - frame->bytes_recvd += copied; - } -} - -static void -ov51x_isoc_irq(struct urb *urb) -{ - int i; - struct usb_ov511 *ov; - struct ov511_sbuf *sbuf; - - if (!urb->context) { - PDEBUG(4, "no context"); - return; - } - - sbuf = urb->context; - ov = sbuf->ov; - - if (!ov || !ov->dev || !ov->user) { - PDEBUG(4, "no device, or not open"); - return; - } - - if (!ov->streaming) { - PDEBUG(4, "hmmm... not streaming, but got interrupt"); - return; - } - - if (urb->status == -ENOENT || urb->status == -ECONNRESET) { - PDEBUG(4, "URB unlinked"); - return; - } - - if (urb->status != -EINPROGRESS && urb->status != 0) { - err("ERROR: urb->status=%d: %s", urb->status, - symbolic(urb_errlist, urb->status)); - } - - /* Copy the data received into our frame buffer */ - PDEBUG(5, "sbuf[%d]: Moving %d packets", sbuf->n, - urb->number_of_packets); - for (i = 0; i < urb->number_of_packets; i++) { - /* Warning: Don't call *_move_data() if no frame active! */ - if (ov->curframe >= 0) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - unsigned char *cdata; - - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = 0; - - cdata = urb->transfer_buffer - + urb->iso_frame_desc[i].offset; - - if (!n) { - PDEBUG(4, "Zero-length packet"); - continue; - } - - if (st) - PDEBUG(2, "data error: [%d] len=%d, status=%d", - i, n, st); - - if (ov->bclass == BCL_OV511) - ov511_move_data(ov, cdata, n); - else if (ov->bclass == BCL_OV518) - ov518_move_data(ov, cdata, n); - else - err("Unknown bridge device (%d)", ov->bridge); - - } else if (waitqueue_active(&ov->wq)) { - wake_up_interruptible(&ov->wq); - } - } - - /* Resubmit this URB */ - urb->dev = ov->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - err("usb_submit_urb() ret %d", i); - - return; -} - -/**************************************************************************** - * - * Stream initialization and termination - * - ***************************************************************************/ - -static int -ov51x_init_isoc(struct usb_ov511 *ov) -{ - struct urb *urb; - int fx, err, n, i, size; - - PDEBUG(3, "*** Initializing capture ***"); - - ov->curframe = -1; - - if (ov->bridge == BRG_OV511) { - if (cams == 1) - size = 993; - else if (cams == 2) - size = 513; - else if (cams == 3 || cams == 4) - size = 257; - else { - err("\"cams\" parameter too high!"); - return -1; - } - } else if (ov->bridge == BRG_OV511PLUS) { - if (cams == 1) - size = 961; - else if (cams == 2) - size = 513; - else if (cams == 3 || cams == 4) - size = 257; - else if (cams >= 5 && cams <= 8) - size = 129; - else if (cams >= 9 && cams <= 31) - size = 33; - else { - err("\"cams\" parameter too high!"); - return -1; - } - } else if (ov->bclass == BCL_OV518) { - if (cams == 1) - size = 896; - else if (cams == 2) - size = 512; - else if (cams == 3 || cams == 4) - size = 256; - else if (cams >= 5 && cams <= 8) - size = 128; - else { - err("\"cams\" parameter too high!"); - return -1; - } - } else { - err("invalid bridge type"); - return -1; - } - - // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now - if (ov->bclass == BCL_OV518) { - if (packetsize == -1) { - ov518_set_packet_size(ov, 640); - } else { - dev_info(&ov->dev->dev, "Forcing packet size to %d\n", - packetsize); - ov518_set_packet_size(ov, packetsize); - } - } else { - if (packetsize == -1) { - ov511_set_packet_size(ov, size); - } else { - dev_info(&ov->dev->dev, "Forcing packet size to %d\n", - packetsize); - ov511_set_packet_size(ov, packetsize); - } - } - - for (n = 0; n < OV511_NUMSBUF; n++) { - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - err("init isoc: usb_alloc_urb ret. NULL"); - for (i = 0; i < n; i++) - usb_free_urb(ov->sbuf[i].urb); - return -ENOMEM; - } - ov->sbuf[n].urb = urb; - urb->dev = ov->dev; - urb->context = &ov->sbuf[n]; - urb->pipe = usb_rcvisocpipe(ov->dev, OV511_ENDPOINT_ADDRESS); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ov->sbuf[n].data; - urb->complete = ov51x_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC; - urb->interval = 1; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = ov->packet_size * fx; - urb->iso_frame_desc[fx].length = ov->packet_size; - } - } - - ov->streaming = 1; - - for (n = 0; n < OV511_NUMSBUF; n++) { - ov->sbuf[n].urb->dev = ov->dev; - err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL); - if (err) { - err("init isoc: usb_submit_urb(%d) ret %d", n, err); - return err; - } - } - - return 0; -} - -static void -ov51x_unlink_isoc(struct usb_ov511 *ov) -{ - int n; - - /* Unschedule all of the iso td's */ - for (n = OV511_NUMSBUF - 1; n >= 0; n--) { - if (ov->sbuf[n].urb) { - usb_kill_urb(ov->sbuf[n].urb); - usb_free_urb(ov->sbuf[n].urb); - ov->sbuf[n].urb = NULL; - } - } -} - -static void -ov51x_stop_isoc(struct usb_ov511 *ov) -{ - if (!ov->streaming || !ov->dev) - return; - - PDEBUG(3, "*** Stopping capture ***"); - - if (ov->bclass == BCL_OV518) - ov518_set_packet_size(ov, 0); - else - ov511_set_packet_size(ov, 0); - - ov->streaming = 0; - - ov51x_unlink_isoc(ov); -} - -static int -ov51x_new_frame(struct usb_ov511 *ov, int framenum) -{ - struct ov511_frame *frame; - int newnum; - - PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum); - - if (!ov->dev) - return -1; - - /* If we're not grabbing a frame right now and the other frame is */ - /* ready to be grabbed into, then use it instead */ - if (ov->curframe == -1) { - newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES; - if (ov->frame[newnum].grabstate == FRAME_READY) - framenum = newnum; - } else - return 0; - - frame = &ov->frame[framenum]; - - PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, - frame->width, frame->height); - - frame->grabstate = FRAME_GRABBING; - frame->scanstate = STATE_SCANNING; - frame->snapshot = 0; - - ov->curframe = framenum; - - /* Make sure it's not too big */ - if (frame->width > ov->maxwidth) - frame->width = ov->maxwidth; - - frame->width &= ~7L; /* Multiple of 8 */ - - if (frame->height > ov->maxheight) - frame->height = ov->maxheight; - - frame->height &= ~3L; /* Multiple of 4 */ - - return 0; -} - -/**************************************************************************** - * - * Buffer management - * - ***************************************************************************/ - -/* - * - You must acquire buf_lock before entering this function. - * - Because this code will free any non-null pointer, you must be sure to null - * them if you explicitly free them somewhere else! - */ -static void -ov51x_do_dealloc(struct usb_ov511 *ov) -{ - int i; - PDEBUG(4, "entered"); - - if (ov->fbuf) { - rvfree(ov->fbuf, OV511_NUMFRAMES - * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); - ov->fbuf = NULL; - } - - vfree(ov->rawfbuf); - ov->rawfbuf = NULL; - - vfree(ov->tempfbuf); - ov->tempfbuf = NULL; - - for (i = 0; i < OV511_NUMSBUF; i++) { - kfree(ov->sbuf[i].data); - ov->sbuf[i].data = NULL; - } - - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].data = NULL; - ov->frame[i].rawdata = NULL; - ov->frame[i].tempdata = NULL; - if (ov->frame[i].compbuf) { - free_page((unsigned long) ov->frame[i].compbuf); - ov->frame[i].compbuf = NULL; - } - } - - PDEBUG(4, "buffer memory deallocated"); - ov->buf_state = BUF_NOT_ALLOCATED; - PDEBUG(4, "leaving"); -} - -static int -ov51x_alloc(struct usb_ov511 *ov) -{ - int i; - const int w = ov->maxwidth; - const int h = ov->maxheight; - const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE(w, h); - const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h); - - PDEBUG(4, "entered"); - mutex_lock(&ov->buf_lock); - - if (ov->buf_state == BUF_ALLOCATED) - goto out; - - ov->fbuf = rvmalloc(data_bufsize); - if (!ov->fbuf) - goto error; - - ov->rawfbuf = vmalloc(raw_bufsize); - if (!ov->rawfbuf) - goto error; - - memset(ov->rawfbuf, 0, raw_bufsize); - - ov->tempfbuf = vmalloc(raw_bufsize); - if (!ov->tempfbuf) - goto error; - - memset(ov->tempfbuf, 0, raw_bufsize); - - for (i = 0; i < OV511_NUMSBUF; i++) { - ov->sbuf[i].data = kmalloc(FRAMES_PER_DESC * - MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov->sbuf[i].data) - goto error; - - PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data); - } - - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h); - ov->frame[i].rawdata = ov->rawfbuf - + i * MAX_RAW_DATA_SIZE(w, h); - ov->frame[i].tempdata = ov->tempfbuf - + i * MAX_RAW_DATA_SIZE(w, h); - - ov->frame[i].compbuf = - (unsigned char *) __get_free_page(GFP_KERNEL); - if (!ov->frame[i].compbuf) - goto error; - - PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data); - } - - ov->buf_state = BUF_ALLOCATED; -out: - mutex_unlock(&ov->buf_lock); - PDEBUG(4, "leaving"); - return 0; -error: - ov51x_do_dealloc(ov); - mutex_unlock(&ov->buf_lock); - PDEBUG(4, "errored"); - return -ENOMEM; -} - -static void -ov51x_dealloc(struct usb_ov511 *ov) -{ - PDEBUG(4, "entered"); - mutex_lock(&ov->buf_lock); - ov51x_do_dealloc(ov); - mutex_unlock(&ov->buf_lock); - PDEBUG(4, "leaving"); -} - -/**************************************************************************** - * - * V4L 1 API - * - ***************************************************************************/ - -static int -ov51x_v4l1_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct usb_ov511 *ov = video_get_drvdata(vdev); - int err, i; - - PDEBUG(4, "opening"); - - mutex_lock(&ov->lock); - - err = -EBUSY; - if (ov->user) - goto out; - - ov->sub_flag = 0; - - /* In case app doesn't set them... */ - err = ov51x_set_default_params(ov); - if (err < 0) - goto out; - - /* Make sure frames are reset */ - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].grabstate = FRAME_UNUSED; - ov->frame[i].bytes_read = 0; - } - - /* If compression is on, make sure now that a - * decompressor can be loaded */ - if (ov->compress && !ov->decomp_ops) { - err = request_decompressor(ov); - if (err && !dumppix) - goto out; - } - - err = ov51x_alloc(ov); - if (err < 0) - goto out; - - err = ov51x_init_isoc(ov); - if (err) { - ov51x_dealloc(ov); - goto out; - } - - ov->user++; - file->private_data = vdev; - - if (ov->led_policy == LED_AUTO) - ov51x_led_control(ov, 1); - -out: - mutex_unlock(&ov->lock); - return err; -} - -static int -ov51x_v4l1_close(struct file *file) -{ - struct video_device *vdev = file->private_data; - struct usb_ov511 *ov = video_get_drvdata(vdev); - - PDEBUG(4, "ov511_close"); - - mutex_lock(&ov->lock); - - ov->user--; - ov51x_stop_isoc(ov); - - if (ov->led_policy == LED_AUTO) - ov51x_led_control(ov, 0); - - if (ov->dev) - ov51x_dealloc(ov); - - mutex_unlock(&ov->lock); - - /* Device unplugged while open. Only a minimum of unregistration is done - * here; the disconnect callback already did the rest. */ - if (!ov->dev) { - mutex_lock(&ov->cbuf_lock); - kfree(ov->cbuf); - ov->cbuf = NULL; - mutex_unlock(&ov->cbuf_lock); - - ov51x_dealloc(ov); - kfree(ov); - ov = NULL; - } - - file->private_data = NULL; - return 0; -} - -/* Do not call this function directly! */ -static long -ov51x_v4l1_ioctl_internal(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct usb_ov511 *ov = video_get_drvdata(vdev); - PDEBUG(5, "IOCtl: 0x%X", cmd); - - if (!ov->dev) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - - PDEBUG(4, "VIDIOCGCAP"); - - memset(b, 0, sizeof(struct video_capability)); - sprintf(b->name, "%s USB Camera", - symbolic(brglist, ov->bridge)); - b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - b->channels = ov->num_inputs; - b->audios = 0; - b->maxwidth = ov->maxwidth; - b->maxheight = ov->maxheight; - b->minwidth = ov->minwidth; - b->minheight = ov->minheight; - - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - PDEBUG(4, "VIDIOCGCHAN"); - - if ((unsigned)(v->channel) >= ov->num_inputs) { - err("Invalid channel (%d)", v->channel); - return -EINVAL; - } - - v->norm = ov->norm; - v->type = VIDEO_TYPE_CAMERA; - v->flags = 0; -// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0; - v->tuners = 0; - decoder_get_input_name(ov, v->channel, v->name); - - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - int err; - - PDEBUG(4, "VIDIOCSCHAN"); - - /* Make sure it's not a camera */ - if (!ov->has_decoder) { - if (v->channel == 0) - return 0; - else - return -EINVAL; - } - - if (v->norm != VIDEO_MODE_PAL && - v->norm != VIDEO_MODE_NTSC && - v->norm != VIDEO_MODE_SECAM && - v->norm != VIDEO_MODE_AUTO) { - err("Invalid norm (%d)", v->norm); - return -EINVAL; - } - - if ((unsigned)(v->channel) >= ov->num_inputs) { - err("Invalid channel (%d)", v->channel); - return -EINVAL; - } - - err = decoder_set_input(ov, v->channel); - if (err) - return err; - - err = decoder_set_norm(ov, v->norm); - if (err) - return err; - - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - - PDEBUG(4, "VIDIOCGPICT"); - - memset(p, 0, sizeof(struct video_picture)); - if (sensor_get_picture(ov, p)) - return -EIO; - - /* Can we get these from frame[0]? -claudio? */ - p->depth = ov->frame[0].depth; - p->palette = ov->frame[0].format; - - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - int i, rc; - - PDEBUG(4, "VIDIOCSPICT"); - - if (!get_depth(p->palette)) - return -EINVAL; - - if (sensor_set_picture(ov, p)) - return -EIO; - - if (force_palette && p->palette != force_palette) { - dev_info(&ov->dev->dev, "Palette rejected (%s)\n", - symbolic(v4l1_plist, p->palette)); - return -EINVAL; - } - - // FIXME: Format should be independent of frames - if (p->palette != ov->frame[0].format) { - PDEBUG(4, "Detected format change"); - - rc = ov51x_wait_frames_inactive(ov); - if (rc) - return rc; - - mode_init_regs(ov, ov->frame[0].width, - ov->frame[0].height, p->palette, ov->sub_flag); - } - - PDEBUG(4, "Setting depth=%d, palette=%s", - p->depth, symbolic(v4l1_plist, p->palette)); - - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].depth = p->depth; - ov->frame[i].format = p->palette; - } - - return 0; - } - case VIDIOCGCAPTURE: - { - int *vf = arg; - - PDEBUG(4, "VIDIOCGCAPTURE"); - - ov->sub_flag = *vf; - return 0; - } - case VIDIOCSCAPTURE: - { - struct video_capture *vc = arg; - - PDEBUG(4, "VIDIOCSCAPTURE"); - - if (vc->flags) - return -EINVAL; - if (vc->decimation) - return -EINVAL; - - vc->x &= ~3L; - vc->y &= ~1L; - vc->y &= ~31L; - - if (vc->width == 0) - vc->width = 32; - - vc->height /= 16; - vc->height *= 16; - if (vc->height == 0) - vc->height = 16; - - ov->subx = vc->x; - ov->suby = vc->y; - ov->subw = vc->width; - ov->subh = vc->height; - - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - int i, rc; - - PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height); - -#if 0 - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (vw->height != ov->maxheight) - return -EINVAL; - if (vw->width != ov->maxwidth) - return -EINVAL; -#endif - - rc = ov51x_wait_frames_inactive(ov); - if (rc) - return rc; - - rc = mode_init_regs(ov, vw->width, vw->height, - ov->frame[0].format, ov->sub_flag); - if (rc < 0) - return rc; - - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].width = vw->width; - ov->frame[i].height = vw->height; - } - - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - memset(vw, 0, sizeof(struct video_window)); - vw->x = 0; /* FIXME */ - vw->y = 0; - vw->width = ov->frame[0].width; - vw->height = ov->frame[0].height; - vw->flags = 30; - - PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height); - - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - PDEBUG(4, "VIDIOCGMBUF"); - - memset(vm, 0, sizeof(struct video_mbuf)); - vm->size = OV511_NUMFRAMES - * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); - vm->frames = OV511_NUMFRAMES; - - vm->offsets[0] = 0; - for (i = 1; i < OV511_NUMFRAMES; i++) { - vm->offsets[i] = vm->offsets[i-1] - + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); - } - - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - int rc, depth; - unsigned int f = vm->frame; - - PDEBUG(4, "VIDIOCMCAPTURE: frame: %d, %dx%d, %s", f, vm->width, - vm->height, symbolic(v4l1_plist, vm->format)); - - depth = get_depth(vm->format); - if (!depth) { - PDEBUG(2, "VIDIOCMCAPTURE: invalid format (%s)", - symbolic(v4l1_plist, vm->format)); - return -EINVAL; - } - - if (f >= OV511_NUMFRAMES) { - err("VIDIOCMCAPTURE: invalid frame (%d)", f); - return -EINVAL; - } - - if (vm->width > ov->maxwidth - || vm->height > ov->maxheight) { - err("VIDIOCMCAPTURE: requested dimensions too big"); - return -EINVAL; - } - - if (ov->frame[f].grabstate == FRAME_GRABBING) { - PDEBUG(4, "VIDIOCMCAPTURE: already grabbing"); - return -EBUSY; - } - - if (force_palette && (vm->format != force_palette)) { - PDEBUG(2, "palette rejected (%s)", - symbolic(v4l1_plist, vm->format)); - return -EINVAL; - } - - if ((ov->frame[f].width != vm->width) || - (ov->frame[f].height != vm->height) || - (ov->frame[f].format != vm->format) || - (ov->frame[f].sub_flag != ov->sub_flag) || - (ov->frame[f].depth != depth)) { - PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters"); - - rc = ov51x_wait_frames_inactive(ov); - if (rc) - return rc; - - rc = mode_init_regs(ov, vm->width, vm->height, - vm->format, ov->sub_flag); -#if 0 - if (rc < 0) { - PDEBUG(1, "Got error while initializing regs "); - return ret; - } -#endif - ov->frame[f].width = vm->width; - ov->frame[f].height = vm->height; - ov->frame[f].format = vm->format; - ov->frame[f].sub_flag = ov->sub_flag; - ov->frame[f].depth = depth; - } - - /* Mark it as ready */ - ov->frame[f].grabstate = FRAME_READY; - - PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", f); - - return ov51x_new_frame(ov, f); - } - case VIDIOCSYNC: - { - unsigned int fnum = *((unsigned int *) arg); - struct ov511_frame *frame; - int rc; - - if (fnum >= OV511_NUMFRAMES) { - err("VIDIOCSYNC: invalid frame (%d)", fnum); - return -EINVAL; - } - - frame = &ov->frame[fnum]; - - PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum, - frame->grabstate); - - switch (frame->grabstate) { - case FRAME_UNUSED: - return -EINVAL; - case FRAME_READY: - case FRAME_GRABBING: - case FRAME_ERROR: -redo: - if (!ov->dev) - return -EIO; - - rc = wait_event_interruptible(frame->wq, - (frame->grabstate == FRAME_DONE) - || (frame->grabstate == FRAME_ERROR)); - - if (rc) - return rc; - - if (frame->grabstate == FRAME_ERROR) { - if ((rc = ov51x_new_frame(ov, fnum)) < 0) - return rc; - goto redo; - } - /* Fall through */ - case FRAME_DONE: - if (ov->snap_enabled && !frame->snapshot) { - if ((rc = ov51x_new_frame(ov, fnum)) < 0) - return rc; - goto redo; - } - - frame->grabstate = FRAME_UNUSED; - - /* Reset the hardware snapshot button */ - /* FIXME - Is this the best place for this? */ - if ((ov->snap_enabled) && (frame->snapshot)) { - frame->snapshot = 0; - ov51x_clear_snapshot(ov); - } - - /* Decompression, format conversion, etc... */ - ov51x_postprocess(ov, frame); - - break; - } /* end switch */ - - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - PDEBUG(4, "VIDIOCGFBUF"); - - memset(vb, 0, sizeof(struct video_buffer)); - - return 0; - } - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - PDEBUG(4, "VIDIOCGUNIT"); - - memset(vu, 0, sizeof(struct video_unit)); - - vu->video = ov->vdev->minor; - vu->vbi = VIDEO_NO_UNIT; - vu->radio = VIDEO_NO_UNIT; - vu->audio = VIDEO_NO_UNIT; - vu->teletext = VIDEO_NO_UNIT; - - return 0; - } - case OV511IOC_WI2C: - { - struct ov511_i2c_struct *w = arg; - - return i2c_w_slave(ov, w->slave, w->reg, w->value, w->mask); - } - case OV511IOC_RI2C: - { - struct ov511_i2c_struct *r = arg; - int rc; - - rc = i2c_r_slave(ov, r->slave, r->reg); - if (rc < 0) - return rc; - - r->value = rc; - return 0; - } - default: - PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd); - return -ENOIOCTLCMD; - } /* end switch */ - - return 0; -} - -static long -ov51x_v4l1_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct video_device *vdev = file->private_data; - struct usb_ov511 *ov = video_get_drvdata(vdev); - int rc; - - if (mutex_lock_interruptible(&ov->lock)) - return -EINTR; - - rc = video_usercopy(file, cmd, arg, ov51x_v4l1_ioctl_internal); - - mutex_unlock(&ov->lock); - return rc; -} - -static ssize_t -ov51x_v4l1_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos) -{ - struct video_device *vdev = file->private_data; - int noblock = file->f_flags&O_NONBLOCK; - unsigned long count = cnt; - struct usb_ov511 *ov = video_get_drvdata(vdev); - int i, rc = 0, frmx = -1; - struct ov511_frame *frame; - - if (mutex_lock_interruptible(&ov->lock)) - return -EINTR; - - PDEBUG(4, "%ld bytes, noblock=%d", count, noblock); - - if (!vdev || !buf) { - rc = -EFAULT; - goto error; - } - - if (!ov->dev) { - rc = -EIO; - goto error; - } - -// FIXME: Only supports two frames - /* See if a frame is completed, then use it. */ - if (ov->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ - frmx = 0; - else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ - frmx = 1; - - /* If nonblocking we return immediately */ - if (noblock && (frmx == -1)) { - rc = -EAGAIN; - goto error; - } - - /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ - /* See if a frame is in process (grabbing), then use it. */ - if (frmx == -1) { - if (ov->frame[0].grabstate == FRAME_GRABBING) - frmx = 0; - else if (ov->frame[1].grabstate == FRAME_GRABBING) - frmx = 1; - } - - /* If no frame is active, start one. */ - if (frmx == -1) { - if ((rc = ov51x_new_frame(ov, frmx = 0))) { - err("read: ov51x_new_frame error"); - goto error; - } - } - - frame = &ov->frame[frmx]; - -restart: - if (!ov->dev) { - rc = -EIO; - goto error; - } - - /* Wait while we're grabbing the image */ - PDEBUG(4, "Waiting image grabbing"); - rc = wait_event_interruptible(frame->wq, - (frame->grabstate == FRAME_DONE) - || (frame->grabstate == FRAME_ERROR)); - - if (rc) - goto error; - - PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate); - PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd); - - if (frame->grabstate == FRAME_ERROR) { - frame->bytes_read = 0; - err("** ick! ** Errored frame %d", ov->curframe); - if (ov51x_new_frame(ov, frmx)) { - err("read: ov51x_new_frame error"); - goto error; - } - goto restart; - } - - - /* Repeat until we get a snapshot frame */ - if (ov->snap_enabled) - PDEBUG(4, "Waiting snapshot frame"); - if (ov->snap_enabled && !frame->snapshot) { - frame->bytes_read = 0; - if ((rc = ov51x_new_frame(ov, frmx))) { - err("read: ov51x_new_frame error"); - goto error; - } - goto restart; - } - - /* Clear the snapshot */ - if (ov->snap_enabled && frame->snapshot) { - frame->snapshot = 0; - ov51x_clear_snapshot(ov); - } - - /* Decompression, format conversion, etc... */ - ov51x_postprocess(ov, frame); - - PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx, - frame->bytes_read, - get_frame_length(frame)); - - /* copy bytes to user space; we allow for partials reads */ -// if ((count + frame->bytes_read) -// > get_frame_length((struct ov511_frame *)frame)) -// count = frame->scanlength - frame->bytes_read; - - /* FIXME - count hardwired to be one frame... */ - count = get_frame_length(frame); - - PDEBUG(4, "Copy to user space: %ld bytes", count); - if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) { - PDEBUG(4, "Copy failed! %d bytes not copied", i); - rc = -EFAULT; - goto error; - } - - frame->bytes_read += count; - PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld", - count, frame->bytes_read); - - /* If all data have been read... */ - if (frame->bytes_read - >= get_frame_length(frame)) { - frame->bytes_read = 0; - -// FIXME: Only supports two frames - /* Mark it as available to be used again. */ - ov->frame[frmx].grabstate = FRAME_UNUSED; - if ((rc = ov51x_new_frame(ov, !frmx))) { - err("ov51x_new_frame returned error"); - goto error; - } - } - - PDEBUG(4, "read finished, returning %ld (sweet)", count); - - mutex_unlock(&ov->lock); - return count; - -error: - mutex_unlock(&ov->lock); - return rc; -} - -static int -ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - struct usb_ov511 *ov = video_get_drvdata(vdev); - unsigned long page, pos; - - if (ov->dev == NULL) - return -EIO; - - PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); - - if (size > (((OV511_NUMFRAMES - * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight) - + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))) - return -EINVAL; - - if (mutex_lock_interruptible(&ov->lock)) - return -EINTR; - - pos = (unsigned long)ov->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&ov->lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - mutex_unlock(&ov->lock); - return 0; -} - -static const struct v4l2_file_operations ov511_fops = { - .owner = THIS_MODULE, - .open = ov51x_v4l1_open, - .release = ov51x_v4l1_close, - .read = ov51x_v4l1_read, - .mmap = ov51x_v4l1_mmap, - .ioctl = ov51x_v4l1_ioctl, -}; - -static struct video_device vdev_template = { - .name = "OV511 USB Camera", - .fops = &ov511_fops, - .release = video_device_release, -}; - -/**************************************************************************** - * - * OV511 and sensor configuration - * - ***************************************************************************/ - -/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses - * the same register settings as the OV7610, since they are very similar. - */ -static int -ov7xx0_configure(struct usb_ov511 *ov) -{ - int i, success; - int rc; - - /* Lawrence Glaister <lg@jfm.bc.ca> reports: - * - * Register 0x0f in the 7610 has the following effects: - * - * 0x85 (AEC method 1): Best overall, good contrast range - * 0x45 (AEC method 2): Very overexposed - * 0xa5 (spec sheet default): Ok, but the black level is - * shifted resulting in loss of contrast - * 0x05 (old driver setting): very overexposed, too much - * contrast - */ - static struct ov511_regvals aRegvalsNorm7610[] = { - { OV511_I2C_BUS, 0x10, 0xff }, - { OV511_I2C_BUS, 0x16, 0x06 }, - { OV511_I2C_BUS, 0x28, 0x24 }, - { OV511_I2C_BUS, 0x2b, 0xac }, - { OV511_I2C_BUS, 0x12, 0x00 }, - { OV511_I2C_BUS, 0x38, 0x81 }, - { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ - { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ - { OV511_I2C_BUS, 0x15, 0x01 }, - { OV511_I2C_BUS, 0x20, 0x1c }, - { OV511_I2C_BUS, 0x23, 0x2a }, - { OV511_I2C_BUS, 0x24, 0x10 }, - { OV511_I2C_BUS, 0x25, 0x8a }, - { OV511_I2C_BUS, 0x26, 0xa2 }, - { OV511_I2C_BUS, 0x27, 0xc2 }, - { OV511_I2C_BUS, 0x2a, 0x04 }, - { OV511_I2C_BUS, 0x2c, 0xfe }, - { OV511_I2C_BUS, 0x2d, 0x93 }, - { OV511_I2C_BUS, 0x30, 0x71 }, - { OV511_I2C_BUS, 0x31, 0x60 }, - { OV511_I2C_BUS, 0x32, 0x26 }, - { OV511_I2C_BUS, 0x33, 0x20 }, - { OV511_I2C_BUS, 0x34, 0x48 }, - { OV511_I2C_BUS, 0x12, 0x24 }, - { OV511_I2C_BUS, 0x11, 0x01 }, - { OV511_I2C_BUS, 0x0c, 0x24 }, - { OV511_I2C_BUS, 0x0d, 0x24 }, - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - - static struct ov511_regvals aRegvalsNorm7620[] = { - { OV511_I2C_BUS, 0x00, 0x00 }, - { OV511_I2C_BUS, 0x01, 0x80 }, - { OV511_I2C_BUS, 0x02, 0x80 }, - { OV511_I2C_BUS, 0x03, 0xc0 }, - { OV511_I2C_BUS, 0x06, 0x60 }, - { OV511_I2C_BUS, 0x07, 0x00 }, - { OV511_I2C_BUS, 0x0c, 0x24 }, - { OV511_I2C_BUS, 0x0c, 0x24 }, - { OV511_I2C_BUS, 0x0d, 0x24 }, - { OV511_I2C_BUS, 0x11, 0x01 }, - { OV511_I2C_BUS, 0x12, 0x24 }, - { OV511_I2C_BUS, 0x13, 0x01 }, - { OV511_I2C_BUS, 0x14, 0x84 }, - { OV511_I2C_BUS, 0x15, 0x01 }, - { OV511_I2C_BUS, 0x16, 0x03 }, - { OV511_I2C_BUS, 0x17, 0x2f }, - { OV511_I2C_BUS, 0x18, 0xcf }, - { OV511_I2C_BUS, 0x19, 0x06 }, - { OV511_I2C_BUS, 0x1a, 0xf5 }, - { OV511_I2C_BUS, 0x1b, 0x00 }, - { OV511_I2C_BUS, 0x20, 0x18 }, - { OV511_I2C_BUS, 0x21, 0x80 }, - { OV511_I2C_BUS, 0x22, 0x80 }, - { OV511_I2C_BUS, 0x23, 0x00 }, - { OV511_I2C_BUS, 0x26, 0xa2 }, - { OV511_I2C_BUS, 0x27, 0xea }, - { OV511_I2C_BUS, 0x28, 0x20 }, - { OV511_I2C_BUS, 0x29, 0x00 }, - { OV511_I2C_BUS, 0x2a, 0x10 }, - { OV511_I2C_BUS, 0x2b, 0x00 }, - { OV511_I2C_BUS, 0x2c, 0x88 }, - { OV511_I2C_BUS, 0x2d, 0x91 }, - { OV511_I2C_BUS, 0x2e, 0x80 }, - { OV511_I2C_BUS, 0x2f, 0x44 }, - { OV511_I2C_BUS, 0x60, 0x27 }, - { OV511_I2C_BUS, 0x61, 0x02 }, - { OV511_I2C_BUS, 0x62, 0x5f }, - { OV511_I2C_BUS, 0x63, 0xd5 }, - { OV511_I2C_BUS, 0x64, 0x57 }, - { OV511_I2C_BUS, 0x65, 0x83 }, - { OV511_I2C_BUS, 0x66, 0x55 }, - { OV511_I2C_BUS, 0x67, 0x92 }, - { OV511_I2C_BUS, 0x68, 0xcf }, - { OV511_I2C_BUS, 0x69, 0x76 }, - { OV511_I2C_BUS, 0x6a, 0x22 }, - { OV511_I2C_BUS, 0x6b, 0x00 }, - { OV511_I2C_BUS, 0x6c, 0x02 }, - { OV511_I2C_BUS, 0x6d, 0x44 }, - { OV511_I2C_BUS, 0x6e, 0x80 }, - { OV511_I2C_BUS, 0x6f, 0x1d }, - { OV511_I2C_BUS, 0x70, 0x8b }, - { OV511_I2C_BUS, 0x71, 0x00 }, - { OV511_I2C_BUS, 0x72, 0x14 }, - { OV511_I2C_BUS, 0x73, 0x54 }, - { OV511_I2C_BUS, 0x74, 0x00 }, - { OV511_I2C_BUS, 0x75, 0x8e }, - { OV511_I2C_BUS, 0x76, 0x00 }, - { OV511_I2C_BUS, 0x77, 0xff }, - { OV511_I2C_BUS, 0x78, 0x80 }, - { OV511_I2C_BUS, 0x79, 0x80 }, - { OV511_I2C_BUS, 0x7a, 0x80 }, - { OV511_I2C_BUS, 0x7b, 0xe2 }, - { OV511_I2C_BUS, 0x7c, 0x00 }, - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - - PDEBUG(4, "starting configuration"); - - /* This looks redundant, but is necessary for WebCam 3 */ - ov->primary_i2c_slave = OV7xx0_SID; - if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) - return -1; - - if (init_ov_sensor(ov) >= 0) { - PDEBUG(1, "OV7xx0 sensor initalized (method 1)"); - } else { - /* Reset the 76xx */ - if (i2c_w(ov, 0x12, 0x80) < 0) - return -1; - - /* Wait for it to initialize */ - msleep(150); - - i = 0; - success = 0; - while (i <= i2c_detect_tries) { - if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && - (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { - success = 1; - break; - } else { - i++; - } - } - -// Was (i == i2c_detect_tries) previously. This obviously used to always report -// success. Whether anyone actually depended on that bug is unknown - if ((i >= i2c_detect_tries) && (success == 0)) { - err("Failed to read sensor ID. You might not have an"); - err("OV7610/20, or it may be not responding. Report"); - err("this to " EMAIL); - err("This is only a warning. You can attempt to use"); - err("your camera anyway"); -// Only issue a warning for now -// return -1; - } else { - PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1); - } - } - - /* Detect sensor (sub)type */ - rc = i2c_r(ov, OV7610_REG_COM_I); - - if (rc < 0) { - err("Error detecting sensor type"); - return -1; - } else if ((rc & 3) == 3) { - dev_info(&ov->dev->dev, "Sensor is an OV7610\n"); - ov->sensor = SEN_OV7610; - } else if ((rc & 3) == 1) { - /* I don't know what's different about the 76BE yet. */ - if (i2c_r(ov, 0x15) & 1) - dev_info(&ov->dev->dev, "Sensor is an OV7620AE\n"); - else - dev_info(&ov->dev->dev, "Sensor is an OV76BE\n"); - - /* OV511+ will return all zero isoc data unless we - * configure the sensor as a 7620. Someone needs to - * find the exact reg. setting that causes this. */ - if (ov->bridge == BRG_OV511PLUS) { - dev_info(&ov->dev->dev, - "Enabling 511+/7620AE workaround\n"); - ov->sensor = SEN_OV7620; - } else { - ov->sensor = SEN_OV76BE; - } - } else if ((rc & 3) == 0) { - dev_info(&ov->dev->dev, "Sensor is an OV7620\n"); - ov->sensor = SEN_OV7620; - } else { - err("Unknown image sensor version: %d", rc & 3); - return -1; - } - - if (ov->sensor == SEN_OV7620) { - PDEBUG(4, "Writing 7620 registers"); - if (write_regvals(ov, aRegvalsNorm7620)) - return -1; - } else { - PDEBUG(4, "Writing 7610 registers"); - if (write_regvals(ov, aRegvalsNorm7610)) - return -1; - } - - /* Set sensor-specific vars */ - ov->maxwidth = 640; - ov->maxheight = 480; - ov->minwidth = 64; - ov->minheight = 48; - - // FIXME: These do not match the actual settings yet - ov->brightness = 0x80 << 8; - ov->contrast = 0x80 << 8; - ov->colour = 0x80 << 8; - ov->hue = 0x80 << 8; - - return 0; -} - -/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ -static int -ov6xx0_configure(struct usb_ov511 *ov) -{ - int rc; - - static struct ov511_regvals aRegvalsNorm6x20[] = { - { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ - { OV511_I2C_BUS, 0x11, 0x01 }, - { OV511_I2C_BUS, 0x03, 0x60 }, - { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ - { OV511_I2C_BUS, 0x07, 0xa8 }, - /* The ratio of 0x0c and 0x0d controls the white point */ - { OV511_I2C_BUS, 0x0c, 0x24 }, - { OV511_I2C_BUS, 0x0d, 0x24 }, - { OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */ - { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */ - { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */ - { OV511_I2C_BUS, 0x14, 0x04 }, - /* 0x16: 0x06 helps frame stability with moving objects */ - { OV511_I2C_BUS, 0x16, 0x06 }, -// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ - { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ - /* 0x28: 0x05 Selects RGB format if RGB on */ - { OV511_I2C_BUS, 0x28, 0x05 }, - { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ -// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ - { OV511_I2C_BUS, 0x2d, 0x99 }, - { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Processing Parameter */ - { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */ - { OV511_I2C_BUS, 0x38, 0x8b }, - { OV511_I2C_BUS, 0x39, 0x40 }, - - { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ - { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ - { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ - - { OV511_I2C_BUS, 0x3d, 0x80 }, - /* These next two registers (0x4a, 0x4b) are undocumented. They - * control the color balance */ - { OV511_I2C_BUS, 0x4a, 0x80 }, - { OV511_I2C_BUS, 0x4b, 0x80 }, - { OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */ - { OV511_I2C_BUS, 0x4e, 0xc1 }, - { OV511_I2C_BUS, 0x4f, 0x04 }, -// Do 50-53 have any effect? -// Toggle 0x12[2] off and on here? - { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */ - }; - - static struct ov511_regvals aRegvalsNorm6x30[] = { - /*OK*/ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ - { OV511_I2C_BUS, 0x11, 0x00 }, - /*OK*/ { OV511_I2C_BUS, 0x03, 0x60 }, - /*0A?*/ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ - { OV511_I2C_BUS, 0x07, 0xa8 }, - /* The ratio of 0x0c and 0x0d controls the white point */ - /*OK*/ { OV511_I2C_BUS, 0x0c, 0x24 }, - /*OK*/ { OV511_I2C_BUS, 0x0d, 0x24 }, - /*A*/ { OV511_I2C_BUS, 0x0e, 0x20 }, -// /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 }, - { OV511_I2C_BUS, 0x16, 0x03 }, -// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ - // 21 & 22? The suggested values look wrong. Go with default - /*A*/ { OV511_I2C_BUS, 0x23, 0xc0 }, - /*A*/ { OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default -// /*OK*/ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ - - /* 0x28: 0x05 Selects RGB format if RGB on */ -// /*04?*/ { OV511_I2C_BUS, 0x28, 0x05 }, -// /*04?*/ { OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus - - /*OK*/ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ -// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ - { OV511_I2C_BUS, 0x2d, 0x99 }, -// /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620 -// /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */ -// /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 }, -// /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7 -// { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ -// { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ -// { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ - { OV511_I2C_BUS, 0x3d, 0x80 }, -// /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e }, - - /* These next two registers (0x4a, 0x4b) are undocumented. They - * control the color balance */ -// /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these -// /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 }, - { OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ - /*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 }, - - /* UV average mode, color killer: strongest */ - { OV511_I2C_BUS, 0x4f, 0x07 }, - - { OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */ - { OV511_I2C_BUS, 0x57, 0x81 }, /* (default) */ - { OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp: +1 */ - { OV511_I2C_BUS, 0x5a, 0x2c }, /* (undocumented) */ - { OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */ -// { OV511_I2C_BUS, 0x5c, 0x10 }, - { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */ - }; - - PDEBUG(4, "starting sensor configuration"); - - if (init_ov_sensor(ov) < 0) { - err("Failed to read sensor ID. You might not have an OV6xx0,"); - err("or it may be not responding. Report this to " EMAIL); - return -1; - } else { - PDEBUG(1, "OV6xx0 sensor detected"); - } - - /* Detect sensor (sub)type */ - rc = i2c_r(ov, OV7610_REG_COM_I); - - if (rc < 0) { - err("Error detecting sensor type"); - return -1; - } - - if ((rc & 3) == 0) { - ov->sensor = SEN_OV6630; - dev_info(&ov->dev->dev, "Sensor is an OV6630\n"); - } else if ((rc & 3) == 1) { - ov->sensor = SEN_OV6620; - dev_info(&ov->dev->dev, "Sensor is an OV6620\n"); - } else if ((rc & 3) == 2) { - ov->sensor = SEN_OV6630; - dev_info(&ov->dev->dev, "Sensor is an OV6630AE\n"); - } else if ((rc & 3) == 3) { - ov->sensor = SEN_OV6630; - dev_info(&ov->dev->dev, "Sensor is an OV6630AF\n"); - } - - /* Set sensor-specific vars */ - ov->maxwidth = 352; - ov->maxheight = 288; - ov->minwidth = 64; - ov->minheight = 48; - - // FIXME: These do not match the actual settings yet - ov->brightness = 0x80 << 8; - ov->contrast = 0x80 << 8; - ov->colour = 0x80 << 8; - ov->hue = 0x80 << 8; - - if (ov->sensor == SEN_OV6620) { - PDEBUG(4, "Writing 6x20 registers"); - if (write_regvals(ov, aRegvalsNorm6x20)) - return -1; - } else { - PDEBUG(4, "Writing 6x30 registers"); - if (write_regvals(ov, aRegvalsNorm6x30)) - return -1; - } - - return 0; -} - -/* This initializes the KS0127 and KS0127B video decoders. */ -static int -ks0127_configure(struct usb_ov511 *ov) -{ - int rc; - -// FIXME: I don't know how to sync or reset it yet -#if 0 - if (ov51x_init_ks_sensor(ov) < 0) { - err("Failed to initialize the KS0127"); - return -1; - } else { - PDEBUG(1, "KS012x(B) sensor detected"); - } -#endif - - /* Detect decoder subtype */ - rc = i2c_r(ov, 0x00); - if (rc < 0) { - err("Error detecting sensor type"); - return -1; - } else if (rc & 0x08) { - rc = i2c_r(ov, 0x3d); - if (rc < 0) { - err("Error detecting sensor type"); - return -1; - } else if ((rc & 0x0f) == 0) { - dev_info(&ov->dev->dev, "Sensor is a KS0127\n"); - ov->sensor = SEN_KS0127; - } else if ((rc & 0x0f) == 9) { - dev_info(&ov->dev->dev, "Sensor is a KS0127B Rev. A\n"); - ov->sensor = SEN_KS0127B; - } - } else { - err("Error: Sensor is an unsupported KS0122"); - return -1; - } - - /* Set sensor-specific vars */ - ov->maxwidth = 640; - ov->maxheight = 480; - ov->minwidth = 64; - ov->minheight = 48; - - // FIXME: These do not match the actual settings yet - ov->brightness = 0x80 << 8; - ov->contrast = 0x80 << 8; - ov->colour = 0x80 << 8; - ov->hue = 0x80 << 8; - - /* This device is not supported yet. Bail out now... */ - err("This sensor is not supported yet."); - return -1; - - return 0; -} - -/* This initializes the SAA7111A video decoder. */ -static int -saa7111a_configure(struct usb_ov511 *ov) -{ - int rc; - - /* Since there is no register reset command, all registers must be - * written, otherwise gives erratic results */ - static struct ov511_regvals aRegvalsNormSAA7111A[] = { - { OV511_I2C_BUS, 0x06, 0xce }, - { OV511_I2C_BUS, 0x07, 0x00 }, - { OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */ - { OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */ - { OV511_I2C_BUS, 0x00, 0x00 }, - { OV511_I2C_BUS, 0x01, 0x00 }, - { OV511_I2C_BUS, 0x03, 0x23 }, - { OV511_I2C_BUS, 0x04, 0x00 }, - { OV511_I2C_BUS, 0x05, 0x00 }, - { OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */ - { OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */ - { OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */ - { OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */ - { OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */ - { OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */ - { OV511_I2C_BUS, 0x0f, 0x00 }, - { OV511_I2C_BUS, 0x11, 0x0c }, - { OV511_I2C_BUS, 0x12, 0x00 }, - { OV511_I2C_BUS, 0x13, 0x00 }, - { OV511_I2C_BUS, 0x14, 0x00 }, - { OV511_I2C_BUS, 0x15, 0x00 }, - { OV511_I2C_BUS, 0x16, 0x00 }, - { OV511_I2C_BUS, 0x17, 0x00 }, - { OV511_I2C_BUS, 0x02, 0xc0 }, /* Composite input 0 */ - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - -// FIXME: I don't know how to sync or reset it yet -#if 0 - if (ov51x_init_saa_sensor(ov) < 0) { - err("Failed to initialize the SAA7111A"); - return -1; - } else { - PDEBUG(1, "SAA7111A sensor detected"); - } -#endif - - /* 640x480 not supported with PAL */ - if (ov->pal) { - ov->maxwidth = 320; - ov->maxheight = 240; /* Even field only */ - } else { - ov->maxwidth = 640; - ov->maxheight = 480; /* Even/Odd fields */ - } - - ov->minwidth = 320; - ov->minheight = 240; /* Even field only */ - - ov->has_decoder = 1; - ov->num_inputs = 8; - ov->norm = VIDEO_MODE_AUTO; - ov->stop_during_set = 0; /* Decoder guarantees stable image */ - - /* Decoder doesn't change these values, so we use these instead of - * acutally reading the registers (which doesn't work) */ - ov->brightness = 0x80 << 8; - ov->contrast = 0x40 << 9; - ov->colour = 0x40 << 9; - ov->hue = 32768; - - PDEBUG(4, "Writing SAA7111A registers"); - if (write_regvals(ov, aRegvalsNormSAA7111A)) - return -1; - - /* Detect version of decoder. This must be done after writing the - * initial regs or the decoder will lock up. */ - rc = i2c_r(ov, 0x00); - - if (rc < 0) { - err("Error detecting sensor version"); - return -1; - } else { - dev_info(&ov->dev->dev, - "Sensor is an SAA7111A (version 0x%x)\n", rc); - ov->sensor = SEN_SAA7111A; - } - - // FIXME: Fix this for OV518(+) - /* Latch to negative edge of clock. Otherwise, we get incorrect - * colors and jitter in the digital signal. */ - if (ov->bclass == BCL_OV511) - reg_w(ov, 0x11, 0x00); - else - dev_warn(&ov->dev->dev, - "SAA7111A not yet supported with OV518/OV518+\n"); - - return 0; -} - -/* This initializes the OV511/OV511+ and the sensor */ -static int -ov511_configure(struct usb_ov511 *ov) -{ - static struct ov511_regvals aRegvalsInit511[] = { - { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, - { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, - { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, - { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, - { OV511_REG_BUS, R51x_SYS_RESET, 0x3f }, - { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, - { OV511_REG_BUS, R51x_SYS_RESET, 0x3d }, - { OV511_DONE_BUS, 0x0, 0x00}, - }; - - static struct ov511_regvals aRegvalsNorm511[] = { - { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0x01 }, - { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, - { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, - { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, - { OV511_REG_BUS, R511_FIFO_OPTS, 0x1f }, - { OV511_REG_BUS, R511_COMP_EN, 0x00 }, - { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - - static struct ov511_regvals aRegvalsNorm511Plus[] = { - { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0xff }, - { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, - { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, - { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, - { OV511_REG_BUS, R511_FIFO_OPTS, 0xff }, - { OV511_REG_BUS, R511_COMP_EN, 0x00 }, - { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - - PDEBUG(4, ""); - - ov->customid = reg_r(ov, R511_SYS_CUST_ID); - if (ov->customid < 0) { - err("Unable to read camera bridge registers"); - goto error; - } - - PDEBUG (1, "CustomID = %d", ov->customid); - ov->desc = symbolic(camlist, ov->customid); - dev_info(&ov->dev->dev, "model: %s\n", ov->desc); - - if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) { - err("Camera type (%d) not recognized", ov->customid); - err("Please notify " EMAIL " of the name,"); - err("manufacturer, model, and this number of your camera."); - err("Also include the output of the detection process."); - } - - if (ov->customid == 70) /* USB Life TV (PAL/SECAM) */ - ov->pal = 1; - - if (write_regvals(ov, aRegvalsInit511)) - goto error; - - if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) - ov51x_led_control(ov, 0); - - /* The OV511+ has undocumented bits in the flow control register. - * Setting it to 0xff fixes the corruption with moving objects. */ - if (ov->bridge == BRG_OV511) { - if (write_regvals(ov, aRegvalsNorm511)) - goto error; - } else if (ov->bridge == BRG_OV511PLUS) { - if (write_regvals(ov, aRegvalsNorm511Plus)) - goto error; - } else { - err("Invalid bridge"); - } - - if (ov511_init_compression(ov)) - goto error; - - ov->packet_numbering = 1; - ov511_set_packet_size(ov, 0); - - ov->snap_enabled = snapshot; - - /* Test for 7xx0 */ - PDEBUG(3, "Testing for 0V7xx0"); - ov->primary_i2c_slave = OV7xx0_SID; - if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) - goto error; - - if (i2c_w(ov, 0x12, 0x80) < 0) { - /* Test for 6xx0 */ - PDEBUG(3, "Testing for 0V6xx0"); - ov->primary_i2c_slave = OV6xx0_SID; - if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) - goto error; - - if (i2c_w(ov, 0x12, 0x80) < 0) { - /* Test for 8xx0 */ - PDEBUG(3, "Testing for 0V8xx0"); - ov->primary_i2c_slave = OV8xx0_SID; - if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) - goto error; - - if (i2c_w(ov, 0x12, 0x80) < 0) { - /* Test for SAA7111A */ - PDEBUG(3, "Testing for SAA7111A"); - ov->primary_i2c_slave = SAA7111A_SID; - if (ov51x_set_slave_ids(ov, SAA7111A_SID) < 0) - goto error; - - if (i2c_w(ov, 0x0d, 0x00) < 0) { - /* Test for KS0127 */ - PDEBUG(3, "Testing for KS0127"); - ov->primary_i2c_slave = KS0127_SID; - if (ov51x_set_slave_ids(ov, KS0127_SID) < 0) - goto error; - - if (i2c_w(ov, 0x10, 0x00) < 0) { - err("Can't determine sensor slave IDs"); - goto error; - } else { - if (ks0127_configure(ov) < 0) { - err("Failed to configure KS0127"); - goto error; - } - } - } else { - if (saa7111a_configure(ov) < 0) { - err("Failed to configure SAA7111A"); - goto error; - } - } - } else { - err("Detected unsupported OV8xx0 sensor"); - goto error; - } - } else { - if (ov6xx0_configure(ov) < 0) { - err("Failed to configure OV6xx0"); - goto error; - } - } - } else { - if (ov7xx0_configure(ov) < 0) { - err("Failed to configure OV7xx0"); - goto error; - } - } - - return 0; - -error: - err("OV511 Config failed"); - - return -EBUSY; -} - -/* This initializes the OV518/OV518+ and the sensor */ -static int -ov518_configure(struct usb_ov511 *ov) -{ - /* For 518 and 518+ */ - static struct ov511_regvals aRegvalsInit518[] = { - { OV511_REG_BUS, R51x_SYS_RESET, 0x40 }, - { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, - { OV511_REG_BUS, R51x_SYS_RESET, 0x3e }, - { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, - { OV511_REG_BUS, R51x_SYS_RESET, 0x00 }, - { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, - { OV511_REG_BUS, 0x46, 0x00 }, - { OV511_REG_BUS, 0x5d, 0x03 }, - { OV511_DONE_BUS, 0x0, 0x00}, - }; - - static struct ov511_regvals aRegvalsNorm518[] = { - { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ - { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ - { OV511_REG_BUS, 0x31, 0x0f }, - { OV511_REG_BUS, 0x5d, 0x03 }, - { OV511_REG_BUS, 0x24, 0x9f }, - { OV511_REG_BUS, 0x25, 0x90 }, - { OV511_REG_BUS, 0x20, 0x00 }, - { OV511_REG_BUS, 0x51, 0x04 }, - { OV511_REG_BUS, 0x71, 0x19 }, - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - - static struct ov511_regvals aRegvalsNorm518Plus[] = { - { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ - { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ - { OV511_REG_BUS, 0x31, 0x0f }, - { OV511_REG_BUS, 0x5d, 0x03 }, - { OV511_REG_BUS, 0x24, 0x9f }, - { OV511_REG_BUS, 0x25, 0x90 }, - { OV511_REG_BUS, 0x20, 0x60 }, - { OV511_REG_BUS, 0x51, 0x02 }, - { OV511_REG_BUS, 0x71, 0x19 }, - { OV511_REG_BUS, 0x40, 0xff }, - { OV511_REG_BUS, 0x41, 0x42 }, - { OV511_REG_BUS, 0x46, 0x00 }, - { OV511_REG_BUS, 0x33, 0x04 }, - { OV511_REG_BUS, 0x21, 0x19 }, - { OV511_REG_BUS, 0x3f, 0x10 }, - { OV511_DONE_BUS, 0x0, 0x00 }, - }; - - PDEBUG(4, ""); - - /* First 5 bits of custom ID reg are a revision ID on OV518 */ - dev_info(&ov->dev->dev, "Device revision %d\n", - 0x1F & reg_r(ov, R511_SYS_CUST_ID)); - - /* Give it the default description */ - ov->desc = symbolic(camlist, 0); - - if (write_regvals(ov, aRegvalsInit518)) - goto error; - - /* Set LED GPIO pin to output mode */ - if (reg_w_mask(ov, 0x57, 0x00, 0x02) < 0) - goto error; - - /* LED is off by default with OV518; have to explicitly turn it on */ - if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) - ov51x_led_control(ov, 0); - else - ov51x_led_control(ov, 1); - - /* Don't require compression if dumppix is enabled; otherwise it's - * required. OV518 has no uncompressed mode, to save RAM. */ - if (!dumppix && !ov->compress) { - ov->compress = 1; - dev_warn(&ov->dev->dev, - "Compression required with OV518...enabling\n"); - } - - if (ov->bridge == BRG_OV518) { - if (write_regvals(ov, aRegvalsNorm518)) - goto error; - } else if (ov->bridge == BRG_OV518PLUS) { - if (write_regvals(ov, aRegvalsNorm518Plus)) - goto error; - } else { - err("Invalid bridge"); - } - - if (reg_w(ov, 0x2f, 0x80) < 0) - goto error; - - if (ov518_init_compression(ov)) - goto error; - - if (ov->bridge == BRG_OV518) - { - struct usb_interface *ifp; - struct usb_host_interface *alt; - __u16 mxps = 0; - - ifp = usb_ifnum_to_if(ov->dev, 0); - if (ifp) { - alt = usb_altnum_to_altsetting(ifp, 7); - if (alt) - mxps = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); - } - - /* Some OV518s have packet numbering by default, some don't */ - if (mxps == 897) - ov->packet_numbering = 1; - else - ov->packet_numbering = 0; - } else { - /* OV518+ has packet numbering turned on by default */ - ov->packet_numbering = 1; - } - - ov518_set_packet_size(ov, 0); - - ov->snap_enabled = snapshot; - - /* Test for 76xx */ - ov->primary_i2c_slave = OV7xx0_SID; - if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) - goto error; - - /* The OV518 must be more aggressive about sensor detection since - * I2C write will never fail if the sensor is not present. We have - * to try to initialize the sensor to detect its presence */ - - if (init_ov_sensor(ov) < 0) { - /* Test for 6xx0 */ - ov->primary_i2c_slave = OV6xx0_SID; - if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) - goto error; - - if (init_ov_sensor(ov) < 0) { - /* Test for 8xx0 */ - ov->primary_i2c_slave = OV8xx0_SID; - if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) - goto error; - - if (init_ov_sensor(ov) < 0) { - err("Can't determine sensor slave IDs"); - goto error; - } else { - err("Detected unsupported OV8xx0 sensor"); - goto error; - } - } else { - if (ov6xx0_configure(ov) < 0) { - err("Failed to configure OV6xx0"); - goto error; - } - } - } else { - if (ov7xx0_configure(ov) < 0) { - err("Failed to configure OV7xx0"); - goto error; - } - } - - ov->maxwidth = 352; - ov->maxheight = 288; - - // The OV518 cannot go as low as the sensor can - ov->minwidth = 160; - ov->minheight = 120; - - return 0; - -error: - err("OV518 Config failed"); - - return -EBUSY; -} - -/**************************************************************************** - * sysfs - ***************************************************************************/ - -static inline struct usb_ov511 *cd_to_ov(struct device *cd) -{ - struct video_device *vdev = to_video_device(cd); - return video_get_drvdata(vdev); -} - -static ssize_t show_custom_id(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%d\n", ov->customid); -} -static DEVICE_ATTR(custom_id, S_IRUGO, show_custom_id, NULL); - -static ssize_t show_model(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%s\n", ov->desc); -} -static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); - -static ssize_t show_bridge(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%s\n", symbolic(brglist, ov->bridge)); -} -static DEVICE_ATTR(bridge, S_IRUGO, show_bridge, NULL); - -static ssize_t show_sensor(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%s\n", symbolic(senlist, ov->sensor)); -} -static DEVICE_ATTR(sensor, S_IRUGO, show_sensor, NULL); - -static ssize_t show_brightness(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - unsigned short x; - - if (!ov->dev) - return -ENODEV; - sensor_get_brightness(ov, &x); - return sprintf(buf, "%d\n", x >> 8); -} -static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); - -static ssize_t show_saturation(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - unsigned short x; - - if (!ov->dev) - return -ENODEV; - sensor_get_saturation(ov, &x); - return sprintf(buf, "%d\n", x >> 8); -} -static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); - -static ssize_t show_contrast(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - unsigned short x; - - if (!ov->dev) - return -ENODEV; - sensor_get_contrast(ov, &x); - return sprintf(buf, "%d\n", x >> 8); -} -static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); - -static ssize_t show_hue(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - unsigned short x; - - if (!ov->dev) - return -ENODEV; - sensor_get_hue(ov, &x); - return sprintf(buf, "%d\n", x >> 8); -} -static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); - -static ssize_t show_exposure(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - unsigned char exp = 0; - - if (!ov->dev) - return -ENODEV; - sensor_get_exposure(ov, &exp); - return sprintf(buf, "%d\n", exp); -} -static DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL); - -static int ov_create_sysfs(struct video_device *vdev) -{ - int rc; - - rc = device_create_file(&vdev->dev, &dev_attr_custom_id); - if (rc) goto err; - rc = device_create_file(&vdev->dev, &dev_attr_model); - if (rc) goto err_id; - rc = device_create_file(&vdev->dev, &dev_attr_bridge); - if (rc) goto err_model; - rc = device_create_file(&vdev->dev, &dev_attr_sensor); - if (rc) goto err_bridge; - rc = device_create_file(&vdev->dev, &dev_attr_brightness); - if (rc) goto err_sensor; - rc = device_create_file(&vdev->dev, &dev_attr_saturation); - if (rc) goto err_bright; - rc = device_create_file(&vdev->dev, &dev_attr_contrast); - if (rc) goto err_sat; - rc = device_create_file(&vdev->dev, &dev_attr_hue); - if (rc) goto err_contrast; - rc = device_create_file(&vdev->dev, &dev_attr_exposure); - if (rc) goto err_hue; - - return 0; - -err_hue: - device_remove_file(&vdev->dev, &dev_attr_hue); -err_contrast: - device_remove_file(&vdev->dev, &dev_attr_contrast); -err_sat: - device_remove_file(&vdev->dev, &dev_attr_saturation); -err_bright: - device_remove_file(&vdev->dev, &dev_attr_brightness); -err_sensor: - device_remove_file(&vdev->dev, &dev_attr_sensor); -err_bridge: - device_remove_file(&vdev->dev, &dev_attr_bridge); -err_model: - device_remove_file(&vdev->dev, &dev_attr_model); -err_id: - device_remove_file(&vdev->dev, &dev_attr_custom_id); -err: - return rc; -} - -/**************************************************************************** - * USB routines - ***************************************************************************/ - -static int -ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_interface_descriptor *idesc; - struct usb_ov511 *ov; - int i, rc, nr; - - PDEBUG(1, "probing for device..."); - - /* We don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - idesc = &intf->cur_altsetting->desc; - - if (idesc->bInterfaceClass != 0xFF) - return -ENODEV; - if (idesc->bInterfaceSubClass != 0x00) - return -ENODEV; - - if ((ov = kzalloc(sizeof(*ov), GFP_KERNEL)) == NULL) { - err("couldn't kmalloc ov struct"); - goto error_out; - } - - ov->dev = dev; - ov->iface = idesc->bInterfaceNumber; - ov->led_policy = led; - ov->compress = compress; - ov->lightfreq = lightfreq; - ov->num_inputs = 1; /* Video decoder init functs. change this */ - ov->stop_during_set = !fastset; - ov->backlight = backlight; - ov->mirror = mirror; - ov->auto_brt = autobright; - ov->auto_gain = autogain; - ov->auto_exp = autoexp; - - switch (le16_to_cpu(dev->descriptor.idProduct)) { - case PROD_OV511: - ov->bridge = BRG_OV511; - ov->bclass = BCL_OV511; - break; - case PROD_OV511PLUS: - ov->bridge = BRG_OV511PLUS; - ov->bclass = BCL_OV511; - break; - case PROD_OV518: - ov->bridge = BRG_OV518; - ov->bclass = BCL_OV518; - break; - case PROD_OV518PLUS: - ov->bridge = BRG_OV518PLUS; - ov->bclass = BCL_OV518; - break; - case PROD_ME2CAM: - if (le16_to_cpu(dev->descriptor.idVendor) != VEND_MATTEL) - goto error; - ov->bridge = BRG_OV511PLUS; - ov->bclass = BCL_OV511; - break; - default: - err("Unknown product ID 0x%04x", le16_to_cpu(dev->descriptor.idProduct)); - goto error; - } - - dev_info(&intf->dev, "USB %s video device found\n", - symbolic(brglist, ov->bridge)); - - init_waitqueue_head(&ov->wq); - - mutex_init(&ov->lock); /* to 1 == available */ - mutex_init(&ov->buf_lock); - mutex_init(&ov->i2c_lock); - mutex_init(&ov->cbuf_lock); - - ov->buf_state = BUF_NOT_ALLOCATED; - - if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) { - err("usb_make_path error"); - goto error; - } - - /* Allocate control transfer buffer. */ - /* Must be kmalloc()'ed, for DMA compatibility */ - ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL); - if (!ov->cbuf) - goto error; - - if (ov->bclass == BCL_OV518) { - if (ov518_configure(ov) < 0) - goto error; - } else { - if (ov511_configure(ov) < 0) - goto error; - } - - for (i = 0; i < OV511_NUMFRAMES; i++) { - ov->frame[i].framenum = i; - init_waitqueue_head(&ov->frame[i].wq); - } - - for (i = 0; i < OV511_NUMSBUF; i++) { - ov->sbuf[i].ov = ov; - spin_lock_init(&ov->sbuf[i].lock); - ov->sbuf[i].n = i; - } - - /* Unnecessary? (This is done on open(). Need to make sure variables - * are properly initialized without this before removing it, though). */ - if (ov51x_set_default_params(ov) < 0) - goto error; - -#ifdef OV511_DEBUG - if (dump_bridge) { - if (ov->bclass == BCL_OV511) - ov511_dump_regs(ov); - else - ov518_dump_regs(ov); - } -#endif - - ov->vdev = video_device_alloc(); - if (!ov->vdev) - goto error; - - memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); - ov->vdev->parent = &intf->dev; - video_set_drvdata(ov->vdev, ov); - - mutex_lock(&ov->lock); - - /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&ov511_devused, OV511_MAX_UNIT_VIDEO); - - /* Registers device */ - if (unit_video[nr] != 0) - rc = video_register_device(ov->vdev, VFL_TYPE_GRABBER, - unit_video[nr]); - else - rc = video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1); - - if (rc < 0) { - err("video_register_device failed"); - mutex_unlock(&ov->lock); - goto error; - } - - /* Mark device as used */ - ov511_devused |= 1 << nr; - ov->nr = nr; - - dev_info(&intf->dev, "Device at %s registered to %s\n", - ov->usb_path, video_device_node_name(ov->vdev)); - - usb_set_intfdata(intf, ov); - if (ov_create_sysfs(ov->vdev)) { - err("ov_create_sysfs failed"); - ov511_devused &= ~(1 << nr); - mutex_unlock(&ov->lock); - goto error; - } - - mutex_unlock(&ov->lock); - - return 0; - -error: - if (ov->vdev) { - if (!video_is_registered(ov->vdev)) - video_device_release(ov->vdev); - else - video_unregister_device(ov->vdev); - ov->vdev = NULL; - } - - if (ov->cbuf) { - mutex_lock(&ov->cbuf_lock); - kfree(ov->cbuf); - ov->cbuf = NULL; - mutex_unlock(&ov->cbuf_lock); - } - - kfree(ov); - ov = NULL; - -error_out: - err("Camera initialization failed"); - return -EIO; -} - -static void -ov51x_disconnect(struct usb_interface *intf) -{ - struct usb_ov511 *ov = usb_get_intfdata(intf); - int n; - - PDEBUG(3, ""); - - mutex_lock(&ov->lock); - usb_set_intfdata (intf, NULL); - - /* Free device number */ - ov511_devused &= ~(1 << ov->nr); - - if (ov->vdev) - video_unregister_device(ov->vdev); - - for (n = 0; n < OV511_NUMFRAMES; n++) - ov->frame[n].grabstate = FRAME_ERROR; - - ov->curframe = -1; - - /* This will cause the process to request another frame */ - for (n = 0; n < OV511_NUMFRAMES; n++) - wake_up_interruptible(&ov->frame[n].wq); - - wake_up_interruptible(&ov->wq); - - ov->streaming = 0; - ov51x_unlink_isoc(ov); - mutex_unlock(&ov->lock); - - ov->dev = NULL; - - /* Free the memory */ - if (!ov->user) { - mutex_lock(&ov->cbuf_lock); - kfree(ov->cbuf); - ov->cbuf = NULL; - mutex_unlock(&ov->cbuf_lock); - - ov51x_dealloc(ov); - kfree(ov); - ov = NULL; - } - - PDEBUG(3, "Disconnect complete"); -} - -static struct usb_driver ov511_driver = { - .name = "ov511", - .id_table = device_table, - .probe = ov51x_probe, - .disconnect = ov51x_disconnect -}; - -/**************************************************************************** - * - * Module routines - * - ***************************************************************************/ - -static int __init -usb_ov511_init(void) -{ - int retval; - - retval = usb_register(&ov511_driver); - if (retval) - goto out; - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - -out: - return retval; -} - -static void __exit -usb_ov511_exit(void) -{ - usb_deregister(&ov511_driver); - printk(KERN_INFO KBUILD_MODNAME ": driver deregistered\n"); -} - -module_init(usb_ov511_init); -module_exit(usb_ov511_exit); - diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h deleted file mode 100644 index c450c92468d..00000000000 --- a/drivers/media/video/ov511.h +++ /dev/null @@ -1,573 +0,0 @@ -#ifndef __LINUX_OV511_H -#define __LINUX_OV511_H - -#include <asm/uaccess.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <linux/usb.h> -#include <linux/mutex.h> - -#define OV511_DEBUG /* Turn on debug messages */ - -#ifdef OV511_DEBUG - #define PDEBUG(level, fmt, args...) \ - if (debug >= (level)) \ - printk(KERN_INFO KBUILD_MODNAME "[%s:%d] \n" fmt, \ - __func__, __LINE__ , ## args) -#else - #define PDEBUG(level, fmt, args...) do {} while(0) -#endif - -/* This macro restricts an int variable to an inclusive range */ -#define RESTRICT_TO_RANGE(v,mi,ma) { \ - if ((v) < (mi)) (v) = (mi); \ - else if ((v) > (ma)) (v) = (ma); \ -} - -/* --------------------------------- */ -/* DEFINES FOR OV511 AND OTHER CHIPS */ -/* --------------------------------- */ - -/* USB IDs */ -#define VEND_OMNIVISION 0x05A9 -#define PROD_OV511 0x0511 -#define PROD_OV511PLUS 0xA511 -#define PROD_OV518 0x0518 -#define PROD_OV518PLUS 0xA518 - -#define VEND_MATTEL 0x0813 -#define PROD_ME2CAM 0x0002 - -/* --------------------------------- */ -/* OV51x REGISTER MNEMONICS */ -/* --------------------------------- */ - -/* Camera interface register numbers */ -#define R511_CAM_DELAY 0x10 -#define R511_CAM_EDGE 0x11 -#define R511_CAM_PXCNT 0x12 -#define R511_CAM_LNCNT 0x13 -#define R511_CAM_PXDIV 0x14 -#define R511_CAM_LNDIV 0x15 -#define R511_CAM_UV_EN 0x16 -#define R511_CAM_LINE_MODE 0x17 -#define R511_CAM_OPTS 0x18 - -/* Snapshot mode camera interface register numbers */ -#define R511_SNAP_FRAME 0x19 -#define R511_SNAP_PXCNT 0x1A -#define R511_SNAP_LNCNT 0x1B -#define R511_SNAP_PXDIV 0x1C -#define R511_SNAP_LNDIV 0x1D -#define R511_SNAP_UV_EN 0x1E -#define R511_SNAP_OPTS 0x1F - -/* DRAM register numbers */ -#define R511_DRAM_FLOW_CTL 0x20 -#define R511_DRAM_ARCP 0x21 -#define R511_DRAM_MRC 0x22 -#define R511_DRAM_RFC 0x23 - -/* ISO FIFO register numbers */ -#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ -#define R511_FIFO_OPTS 0x31 - -/* Parallel IO register numbers */ -#define R511_PIO_OPTS 0x38 -#define R511_PIO_DATA 0x39 -#define R511_PIO_BIST 0x3E -#define R518_GPIO_IN 0x55 /* OV518(+) only */ -#define R518_GPIO_OUT 0x56 /* OV518(+) only */ -#define R518_GPIO_CTL 0x57 /* OV518(+) only */ -#define R518_GPIO_PULSE_IN 0x58 /* OV518(+) only */ -#define R518_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */ -#define R518_GPIO_PULSE_POL 0x5a /* OV518(+) only */ -#define R518_GPIO_PULSE_EN 0x5b /* OV518(+) only */ -#define R518_GPIO_RESET 0x5c /* OV518(+) only */ - -/* I2C registers */ -#define R511_I2C_CTL 0x40 -#define R518_I2C_CTL 0x47 /* OV518(+) only */ -#define R51x_I2C_W_SID 0x41 -#define R51x_I2C_SADDR_3 0x42 -#define R51x_I2C_SADDR_2 0x43 -#define R51x_I2C_R_SID 0x44 -#define R51x_I2C_DATA 0x45 -#define R51x_I2C_CLOCK 0x46 -#define R51x_I2C_TIMEOUT 0x47 - -/* I2C snapshot registers */ -#define R511_SI2C_SADDR_3 0x48 -#define R511_SI2C_DATA 0x49 - -/* System control registers */ -#define R51x_SYS_RESET 0x50 - /* Reset type definitions */ -#define OV511_RESET_UDC 0x01 -#define OV511_RESET_I2C 0x02 -#define OV511_RESET_FIFO 0x04 -#define OV511_RESET_OMNICE 0x08 -#define OV511_RESET_DRAM 0x10 -#define OV511_RESET_CAM_INT 0x20 -#define OV511_RESET_OV511 0x40 -#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ -#define OV511_RESET_ALL 0x7F - -#define R511_SYS_CLOCK_DIV 0x51 -#define R51x_SYS_SNAP 0x52 -#define R51x_SYS_INIT 0x53 -#define R511_SYS_PWR_CLK 0x54 /* OV511+/OV518(+) only */ -#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ -#define R511_SYS_USER 0x5E -#define R511_SYS_CUST_ID 0x5F - -/* OmniCE (compression) registers */ -#define R511_COMP_PHY 0x70 -#define R511_COMP_PHUV 0x71 -#define R511_COMP_PVY 0x72 -#define R511_COMP_PVUV 0x73 -#define R511_COMP_QHY 0x74 -#define R511_COMP_QHUV 0x75 -#define R511_COMP_QVY 0x76 -#define R511_COMP_QVUV 0x77 -#define R511_COMP_EN 0x78 -#define R511_COMP_LUT_EN 0x79 -#define R511_COMP_LUT_BEGIN 0x80 - -/* --------------------------------- */ -/* ALTERNATE NUMBERS */ -/* --------------------------------- */ - -/* Alternate numbers for various max packet sizes (OV511 only) */ -#define OV511_ALT_SIZE_992 0 -#define OV511_ALT_SIZE_993 1 -#define OV511_ALT_SIZE_768 2 -#define OV511_ALT_SIZE_769 3 -#define OV511_ALT_SIZE_512 4 -#define OV511_ALT_SIZE_513 5 -#define OV511_ALT_SIZE_257 6 -#define OV511_ALT_SIZE_0 7 - -/* Alternate numbers for various max packet sizes (OV511+ only) */ -#define OV511PLUS_ALT_SIZE_0 0 -#define OV511PLUS_ALT_SIZE_33 1 -#define OV511PLUS_ALT_SIZE_129 2 -#define OV511PLUS_ALT_SIZE_257 3 -#define OV511PLUS_ALT_SIZE_385 4 -#define OV511PLUS_ALT_SIZE_513 5 -#define OV511PLUS_ALT_SIZE_769 6 -#define OV511PLUS_ALT_SIZE_961 7 - -/* Alternate numbers for various max packet sizes (OV518(+) only) */ -#define OV518_ALT_SIZE_0 0 -#define OV518_ALT_SIZE_128 1 -#define OV518_ALT_SIZE_256 2 -#define OV518_ALT_SIZE_384 3 -#define OV518_ALT_SIZE_512 4 -#define OV518_ALT_SIZE_640 5 -#define OV518_ALT_SIZE_768 6 -#define OV518_ALT_SIZE_896 7 - -/* --------------------------------- */ -/* OV7610 REGISTER MNEMONICS */ -/* --------------------------------- */ - -/* OV7610 registers */ -#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ -#define OV7610_REG_BLUE 0x01 /* blue channel balance */ -#define OV7610_REG_RED 0x02 /* red channel balance */ -#define OV7610_REG_SAT 0x03 /* saturation */ - /* 04 reserved */ -#define OV7610_REG_CNT 0x05 /* Y contrast */ -#define OV7610_REG_BRT 0x06 /* Y brightness */ - /* 08-0b reserved */ -#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */ -#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */ -#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */ -#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ -#define OV7610_REG_EXP 0x10 /* manual exposure setting */ -#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */ -#define OV7610_REG_COM_A 0x12 /* misc common regs */ -#define OV7610_REG_COM_B 0x13 /* misc common regs */ -#define OV7610_REG_COM_C 0x14 /* misc common regs */ -#define OV7610_REG_COM_D 0x15 /* misc common regs */ -#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ -#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */ -#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */ -#define OV7610_REG_VWIN_START 0x19 /* vertical window start */ -#define OV7610_REG_VWIN_END 0x1A /* vertical window end */ -#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */ -#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ -#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */ - /* 0e-0f reserved */ -#define OV7610_REG_COM_E 0x20 /* misc common regs */ -#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */ -#define OV7610_REG_UOFFSET 0x22 /* U channel offset */ - /* 23 reserved */ -#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */ -#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */ -#define OV7610_REG_COM_F 0x26 /* misc settings */ -#define OV7610_REG_COM_G 0x27 /* misc settings */ -#define OV7610_REG_COM_H 0x28 /* misc settings */ -#define OV7610_REG_COM_I 0x29 /* misc settings */ -#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ -#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */ -#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */ -#define OV7610_REG_COM_J 0x2D /* misc settings */ -#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */ -#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */ - /* 30-32 reserved */ -#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */ -#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */ -#define OV7610_REG_COM_L 0x35 /* misc settings */ - /* 36-37 reserved */ -#define OV7610_REG_COM_K 0x38 /* misc registers */ - -/* --------------------------------- */ -/* I2C ADDRESSES */ -/* --------------------------------- */ - -#define OV7xx0_SID 0x42 -#define OV6xx0_SID 0xC0 -#define OV8xx0_SID 0xA0 -#define KS0127_SID 0xD8 -#define SAA7111A_SID 0x48 - -/* --------------------------------- */ -/* MISCELLANEOUS DEFINES */ -/* --------------------------------- */ - -#define I2C_CLOCK_PRESCALER 0x03 - -#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ -#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ -#define PIXELS_PER_SEG 256 /* Pixels per segment */ - -#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ - -#define OV511_NUMFRAMES 2 -#if OV511_NUMFRAMES > VIDEO_MAX_FRAME - #error "OV511_NUMFRAMES is too high" -#endif - -#define OV511_NUMSBUF 2 - -/* Control transfers use up to 4 bytes */ -#define OV511_CBUF_SIZE 4 - -/* Size of usb_make_path() buffer */ -#define OV511_USB_PATH_LEN 64 - -/* Bridge types */ -enum { - BRG_UNKNOWN, - BRG_OV511, - BRG_OV511PLUS, - BRG_OV518, - BRG_OV518PLUS, -}; - -/* Bridge classes */ -enum { - BCL_UNKNOWN, - BCL_OV511, - BCL_OV518, -}; - -/* Sensor types */ -enum { - SEN_UNKNOWN, - SEN_OV76BE, - SEN_OV7610, - SEN_OV7620, - SEN_OV7620AE, - SEN_OV6620, - SEN_OV6630, - SEN_OV6630AE, - SEN_OV6630AF, - SEN_OV8600, - SEN_KS0127, - SEN_KS0127B, - SEN_SAA7111A, -}; - -enum { - STATE_SCANNING, /* Scanning for start */ - STATE_HEADER, /* Parsing header */ - STATE_LINES, /* Parsing lines */ -}; - -/* Buffer states */ -enum { - BUF_NOT_ALLOCATED, - BUF_ALLOCATED, -}; - -/* --------- Definition of ioctl interface --------- */ - -#define OV511_INTERFACE_VER 101 - -/* LED options */ -enum { - LED_OFF, - LED_ON, - LED_AUTO, -}; - -/* Raw frame formats */ -enum { - RAWFMT_INVALID, - RAWFMT_YUV400, - RAWFMT_YUV420, - RAWFMT_YUV422, - RAWFMT_GBR422, -}; - -struct ov511_i2c_struct { - unsigned char slave; /* Write slave ID (read ID - 1) */ - unsigned char reg; /* Index of register */ - unsigned char value; /* User sets this w/ write, driver does w/ read */ - unsigned char mask; /* Bits to be changed. Not used with read ops */ -}; - -/* ioctls */ -#define OV511IOC_WI2C _IOW('v', BASE_VIDIOCPRIVATE + 5, \ - struct ov511_i2c_struct) -#define OV511IOC_RI2C _IOWR('v', BASE_VIDIOCPRIVATE + 6, \ - struct ov511_i2c_struct) -/* ------------- End IOCTL interface -------------- */ - -struct usb_ov511; /* Forward declaration */ - -struct ov511_sbuf { - struct usb_ov511 *ov; - unsigned char *data; - struct urb *urb; - spinlock_t lock; - int n; -}; - -enum { - FRAME_UNUSED, /* Unused (no MCAPTURE) */ - FRAME_READY, /* Ready to start grabbing */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_ERROR, /* Something bad happened while processing */ -}; - -struct ov511_regvals { - enum { - OV511_DONE_BUS, - OV511_REG_BUS, - OV511_I2C_BUS, - } bus; - unsigned char reg; - unsigned char val; -}; - -struct ov511_frame { - int framenum; /* Index of this frame */ - unsigned char *data; /* Frame buffer */ - unsigned char *tempdata; /* Temp buffer for multi-stage conversions */ - unsigned char *rawdata; /* Raw camera data buffer */ - unsigned char *compbuf; /* Temp buffer for decompressor */ - - int depth; /* Bytes per pixel */ - int width; /* Width application is expecting */ - int height; /* Height application is expecting */ - - int rawwidth; /* Actual width of frame sent from camera */ - int rawheight; /* Actual height of frame sent from camera */ - - int sub_flag; /* Sub-capture mode for this frame? */ - unsigned int format; /* Format for this frame */ - int compressed; /* Is frame compressed? */ - - volatile int grabstate; /* State of grabbing */ - int scanstate; /* State of scanning */ - - int bytes_recvd; /* Number of image bytes received from camera */ - - long bytes_read; /* Amount that has been read() */ - - wait_queue_head_t wq; /* Processes waiting */ - - int snapshot; /* True if frame was a snapshot */ -}; - -#define DECOMP_INTERFACE_VER 4 - -/* Compression module operations */ -struct ov51x_decomp_ops { - int (*decomp_400)(unsigned char *, unsigned char *, unsigned char *, - int, int, int); - int (*decomp_420)(unsigned char *, unsigned char *, unsigned char *, - int, int, int); - int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *, - int, int, int); - struct module *owner; -}; - -struct usb_ov511 { - struct video_device *vdev; - struct usb_device *dev; - - int customid; - char *desc; - unsigned char iface; - char usb_path[OV511_USB_PATH_LEN]; - - /* Determined by sensor type */ - int maxwidth; - int maxheight; - int minwidth; - int minheight; - - int brightness; - int colour; - int contrast; - int hue; - int whiteness; - int exposure; - int auto_brt; /* Auto brightness enabled flag */ - int auto_gain; /* Auto gain control enabled flag */ - int auto_exp; /* Auto exposure enabled flag */ - int backlight; /* Backlight exposure algorithm flag */ - int mirror; /* Image is reversed horizontally */ - - int led_policy; /* LED: off|on|auto; OV511+ only */ - - struct mutex lock; /* Serializes user-accessible operations */ - int user; /* user count for exclusive use */ - - int streaming; /* Are we streaming Isochronous? */ - int grabbing; /* Are we grabbing? */ - - int compress; /* Should the next frame be compressed? */ - int compress_inited; /* Are compression params uploaded? */ - - int lightfreq; /* Power (lighting) frequency */ - int bandfilt; /* Banding filter enabled flag */ - - unsigned char *fbuf; /* Videodev buffer area */ - unsigned char *tempfbuf; /* Temporary (intermediate) buffer area */ - unsigned char *rawfbuf; /* Raw camera data buffer area */ - - int sub_flag; /* Pix Array subcapture on flag */ - int subx; /* Pix Array subcapture x offset */ - int suby; /* Pix Array subcapture y offset */ - int subw; /* Pix Array subcapture width */ - int subh; /* Pix Array subcapture height */ - - int curframe; /* Current receiving sbuf */ - struct ov511_frame frame[OV511_NUMFRAMES]; - - struct ov511_sbuf sbuf[OV511_NUMSBUF]; - - wait_queue_head_t wq; /* Processes waiting */ - - int snap_enabled; /* Snapshot mode enabled */ - - int bridge; /* Type of bridge (BRG_*) */ - int bclass; /* Class of bridge (BCL_*) */ - int sensor; /* Type of image sensor chip (SEN_*) */ - - int packet_size; /* Frame size per isoc desc */ - int packet_numbering; /* Is ISO frame numbering enabled? */ - - /* Framebuffer/sbuf management */ - int buf_state; - struct mutex buf_lock; - - struct ov51x_decomp_ops *decomp_ops; - - /* Stop streaming while changing picture settings */ - int stop_during_set; - - int stopped; /* Streaming is temporarily paused */ - - /* Video decoder stuff */ - int input; /* Composite, S-VIDEO, etc... */ - int num_inputs; /* Number of inputs */ - int norm; /* NTSC / PAL / SECAM */ - int has_decoder; /* Device has a video decoder */ - int pal; /* Device is designed for PAL resolution */ - - /* ov511 device number ID */ - int nr; /* Stores a device number */ - - /* I2C interface */ - struct mutex i2c_lock; /* Protect I2C controller regs */ - unsigned char primary_i2c_slave; /* I2C write id of sensor */ - - /* Control transaction stuff */ - unsigned char *cbuf; /* Buffer for payload */ - struct mutex cbuf_lock; -}; - -/* Used to represent a list of values and their respective symbolic names */ -struct symbolic_list { - int num; - char *name; -}; - -#define NOT_DEFINED_STR "Unknown" - -/* Returns the name of the matching element in the symbolic_list array. The - * end of the list must be marked with an element that has a NULL name. - */ -static inline char * -symbolic(struct symbolic_list list[], int num) -{ - int i; - - for (i = 0; list[i].name != NULL; i++) - if (list[i].num == num) - return (list[i].name); - - return (NOT_DEFINED_STR); -} - -/* Compression stuff */ - -#define OV511_QUANTABLESIZE 64 -#define OV518_QUANTABLESIZE 32 - -#define OV511_YQUANTABLE { \ - 0, 1, 1, 2, 2, 3, 3, 4, \ - 1, 1, 1, 2, 2, 3, 4, 4, \ - 1, 1, 2, 2, 3, 4, 4, 4, \ - 2, 2, 2, 3, 4, 4, 4, 4, \ - 2, 2, 3, 4, 4, 5, 5, 5, \ - 3, 3, 4, 4, 5, 5, 5, 5, \ - 3, 4, 4, 4, 5, 5, 5, 5, \ - 4, 4, 4, 4, 5, 5, 5, 5 \ -} - -#define OV511_UVQUANTABLE { \ - 0, 2, 2, 3, 4, 4, 4, 4, \ - 2, 2, 2, 4, 4, 4, 4, 4, \ - 2, 2, 3, 4, 4, 4, 4, 4, \ - 3, 4, 4, 4, 4, 4, 4, 4, \ - 4, 4, 4, 4, 4, 4, 4, 4, \ - 4, 4, 4, 4, 4, 4, 4, 4, \ - 4, 4, 4, 4, 4, 4, 4, 4, \ - 4, 4, 4, 4, 4, 4, 4, 4 \ -} - -#define OV518_YQUANTABLE { \ - 5, 4, 5, 6, 6, 7, 7, 7, \ - 5, 5, 5, 5, 6, 7, 7, 7, \ - 6, 6, 6, 6, 7, 7, 7, 8, \ - 7, 7, 6, 7, 7, 7, 8, 8 \ -} - -#define OV518_UVQUANTABLE { \ - 6, 6, 6, 7, 7, 7, 7, 7, \ - 6, 6, 6, 7, 7, 7, 7, 7, \ - 6, 6, 6, 7, 7, 7, 7, 8, \ - 7, 7, 7, 7, 7, 7, 8, 8 \ -} - -#endif diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 34034a71021..25eb5d637ee 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -440,21 +440,21 @@ static const struct regval_list ov772x_vga_regs[] = { */ static const struct ov772x_color_format ov772x_cfmts[] = { { - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .dsp3 = 0x0, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8_LE, + .code = V4L2_MBUS_FMT_YVYU8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .dsp3 = UV_ON, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - .code = V4L2_MBUS_FMT_YUYV8_2X8_BE, + .code = V4L2_MBUS_FMT_UYVY8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .dsp3 = 0x0, .com3 = 0x0, @@ -960,7 +960,7 @@ static int ov772x_g_fmt(struct v4l2_subdev *sd, if (!priv->win || !priv->cfmt) { u32 width = VGA_WIDTH, height = VGA_HEIGHT; int ret = ov772x_set_params(client, &width, &height, - V4L2_MBUS_FMT_YUYV8_2X8_LE); + V4L2_MBUS_FMT_YUYV8_2X8); if (ret < 0) return ret; } diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 7ce9e05b478..40cdfab74cc 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -155,7 +155,7 @@ static const struct ov9640_reg ov9640_regs_rgb[] = { }; static enum v4l2_mbus_pixelcode ov9640_codes[] = { - V4L2_MBUS_FMT_YUYV8_2X8_BE, + V4L2_MBUS_FMT_UYVY8_2X8, V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_MBUS_FMT_RGB565_2X8_LE, }; @@ -430,7 +430,7 @@ static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code, { switch (code) { default: - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: alt->com12 = OV9640_COM12_YUV_AVG; alt->com13 = OV9640_COM13_Y_DELAY_EN | OV9640_COM13_YUV_DLY(0x01); @@ -493,7 +493,7 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, } /* select color matrix configuration for given color encoding */ - if (code == V4L2_MBUS_FMT_YUYV8_2X8_BE) { + if (code == V4L2_MBUS_FMT_UYVY8_2X8) { matrix_regs = ov9640_regs_yuv; matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); } else { @@ -579,8 +579,8 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd, cspace = V4L2_COLORSPACE_SRGB; break; default: - code = V4L2_MBUS_FMT_YUYV8_2X8_BE; - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + code = V4L2_MBUS_FMT_UYVY8_2X8; + case V4L2_MBUS_FMT_UYVY8_2X8: cspace = V4L2_COLORSPACE_JPEG; } @@ -606,8 +606,8 @@ static int ov9640_try_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_SRGB; break; default: - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE; - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + case V4L2_MBUS_FMT_UYVY8_2X8: mf->colorspace = V4L2_COLORSPACE_JPEG; } diff --git a/drivers/media/video/ovcamchip/Makefile b/drivers/media/video/ovcamchip/Makefile deleted file mode 100644 index cba4cdf20f4..00000000000 --- a/drivers/media/video/ovcamchip/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \ - ov76be.o - -obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o diff --git a/drivers/media/video/ovcamchip/ov6x20.c b/drivers/media/video/ovcamchip/ov6x20.c deleted file mode 100644 index c04130dab12..00000000000 --- a/drivers/media/video/ovcamchip/ov6x20.c +++ /dev/null @@ -1,414 +0,0 @@ -/* OmniVision OV6620/OV6120 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_SAT 0x03 /* saturation */ -#define REG_CNT 0x05 /* Y contrast */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ -#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ -#define REG_EXP 0x10 /* exposure */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3A -#define VWSBASE 0x05 -#define VWEBASE 0x06 - -struct ov6x20 { - int auto_brt; - int auto_exp; - int backlight; - int bandfilt; - int mirror; -}; - -/* Initial values for use with OV511/OV511+ cameras */ -static struct ovcamchip_regvals regvals_init_6x20_511[] = { - { 0x12, 0x80 }, /* reset */ - { 0x11, 0x01 }, - { 0x03, 0x60 }, - { 0x05, 0x7f }, /* For when autoadjust is off */ - { 0x07, 0xa8 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x0f, 0x15 }, /* COMS */ - { 0x10, 0x75 }, /* AEC Exposure time */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x14, 0x04 }, - { 0x16, 0x03 }, - { 0x26, 0xb2 }, /* BLC enable */ - /* 0x28: 0x05 Selects RGB format if RGB on */ - { 0x28, 0x05 }, - { 0x2a, 0x04 }, /* Disable framerate adjust */ - { 0x2d, 0x99 }, - { 0x33, 0xa0 }, /* Color Processing Parameter */ - { 0x34, 0xd2 }, /* Max A/D range */ - { 0x38, 0x8b }, - { 0x39, 0x40 }, - - { 0x3c, 0x39 }, /* Enable AEC mode changing */ - { 0x3c, 0x3c }, /* Change AEC mode */ - { 0x3c, 0x24 }, /* Disable AEC mode changing */ - - { 0x3d, 0x80 }, - /* These next two registers (0x4a, 0x4b) are undocumented. They - * control the color balance */ - { 0x4a, 0x80 }, - { 0x4b, 0x80 }, - { 0x4d, 0xd2 }, /* This reduces noise a bit */ - { 0x4e, 0xc1 }, - { 0x4f, 0x04 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* Initial values for use with OV518 cameras */ -static struct ovcamchip_regvals regvals_init_6x20_518[] = { - { 0x12, 0x80 }, /* Do a reset */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x8a }, /* Contrast */ - { 0x0c, 0x24 }, /* AWB blue */ - { 0x0d, 0x24 }, /* AWB red */ - { 0x0e, 0x8d }, /* Additional 2x gain */ - { 0x0f, 0x25 }, /* Black expanding level = 1.3V */ - { 0x11, 0x01 }, /* Clock div. */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x01 }, /* (default) */ - { 0x14, 0x80 }, /* Set reserved bit 7 */ - { 0x15, 0x01 }, /* (default) */ - { 0x16, 0x03 }, /* (default) */ - { 0x17, 0x38 }, /* (default) */ - { 0x18, 0xea }, /* (default) */ - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, /* (default) */ - { 0x1e, 0xc4 }, /* (default) */ - { 0x1f, 0x04 }, /* (default) */ - { 0x20, 0x20 }, /* Enable 1st stage aperture correction */ - { 0x21, 0x10 }, /* Y offset */ - { 0x22, 0x88 }, /* U offset */ - { 0x23, 0xc0 }, /* Set XTAL power level */ - { 0x24, 0x53 }, /* AEC bright ratio */ - { 0x25, 0x7a }, /* AEC black ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, /* Full output range */ - { 0x28, 0x01 }, /* (default) */ - { 0x29, 0x00 }, /* (default) */ - { 0x2a, 0x84 }, /* (default) */ - { 0x2b, 0xa8 }, /* Set custom frame rate */ - { 0x2c, 0xa0 }, /* (reserved) */ - { 0x2d, 0x95 }, /* Enable banding filter */ - { 0x2e, 0x88 }, /* V offset */ - { 0x33, 0x22 }, /* Luminance gamma on */ - { 0x34, 0xc7 }, /* A/D bias */ - { 0x36, 0x12 }, /* (reserved) */ - { 0x37, 0x63 }, /* (reserved) */ - { 0x38, 0x8b }, /* Quick AEC/AEB */ - { 0x39, 0x00 }, /* (default) */ - { 0x3a, 0x0f }, /* (default) */ - { 0x3b, 0x3c }, /* (default) */ - { 0x3c, 0x5c }, /* AEC controls */ - { 0x3d, 0x80 }, /* Drop 1 (bad) frame when AEC change */ - { 0x3e, 0x80 }, /* (default) */ - { 0x3f, 0x02 }, /* (default) */ - { 0x40, 0x10 }, /* (reserved) */ - { 0x41, 0x10 }, /* (reserved) */ - { 0x42, 0x00 }, /* (reserved) */ - { 0x43, 0x7f }, /* (reserved) */ - { 0x44, 0x80 }, /* (reserved) */ - { 0x45, 0x1c }, /* (reserved) */ - { 0x46, 0x1c }, /* (reserved) */ - { 0x47, 0x80 }, /* (reserved) */ - { 0x48, 0x5f }, /* (reserved) */ - { 0x49, 0x00 }, /* (reserved) */ - { 0x4a, 0x00 }, /* Color balance (undocumented) */ - { 0x4b, 0x80 }, /* Color balance (undocumented) */ - { 0x4c, 0x58 }, /* (reserved) */ - { 0x4d, 0xd2 }, /* U *= .938, V *= .838 */ - { 0x4e, 0xa0 }, /* (default) */ - { 0x4f, 0x04 }, /* UV 3-point average */ - { 0x50, 0xff }, /* (reserved) */ - { 0x51, 0x58 }, /* (reserved) */ - { 0x52, 0xc0 }, /* (reserved) */ - { 0x53, 0x42 }, /* (reserved) */ - { 0x27, 0xa6 }, /* Enable manual offset adj. (reg 21 & 22) */ - { 0x12, 0x20 }, - { 0x12, 0x24 }, - - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV6x20 camera chip and relevant variables. */ -static int ov6x20_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x20 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - switch (c->adapter->id) { - case I2C_HW_SMBUS_OV511: - rc = ov_write_regvals(c, regvals_init_6x20_511); - break; - case I2C_HW_SMBUS_OV518: - rc = ov_write_regvals(c, regvals_init_6x20_518); - break; - default: - dev_err(&c->dev, "ov6x20: Unsupported adapter\n"); - rc = -ENODEV; - } - - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov6x20_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov6x20_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x20 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_write(c, REG_CNT, v >> 8); - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_HUE: - rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); - if (rc < 0) - goto out; - - rc = ov_write(c, REG_BLUE, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write(c, 0x2b, sixty?0xa8:0x28); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2a, sixty?0x84:0xa4); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_BACKLIGHT: - { - rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80); - s->backlight = v; - break; - } - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov6x20_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x20 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, REG_CNT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_HUE: - rc = ov_read(c, REG_BLUE, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_BACKLIGHT: - ctl->value = s->backlight; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - /******** QCIF-specific regs ********/ - - ov_write(c, 0x14, win->quarter?0x24:0x04); - - /******** Palette-specific regs ********/ - - /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */ - if (c->adapter->id == I2C_HW_SMBUS_OV518) { - if (win->format == VIDEO_PALETTE_GREY) - ov_write_mask(c, 0x13, 0x00, 0x20); - else - ov_write_mask(c, 0x13, 0x20, 0x20); - } else { - if (win->format == VIDEO_PALETTE_GREY) - ov_write_mask(c, 0x13, 0x20, 0x20); - else - ov_write_mask(c, 0x13, 0x00, 0x20); - } - - /******** Clock programming ********/ - - /* The OV6620 needs special handling. This prevents the - * severe banding that normally occurs */ - - /* Clock down */ - ov_write(c, 0x2a, 0x04); - - ov_write(c, 0x11, win->clockdiv); - - ov_write(c, 0x2a, 0x84); - /* This next setting is critical. It seems to improve - * the gain or the contrast. The "reserved" bits seem - * to have some effect in this case. */ - ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */ - - return 0; -} - -static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov6x20_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 0; - vwscale = 0; - } else { - hwscale = 1; - vwscale = 1; /* The datasheet says 0; it's wrong */ - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov6x20_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov6x20_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov6x20_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov6x20_ops = { - .init = ov6x20_init, - .free = ov6x20_free, - .command = ov6x20_command, -}; diff --git a/drivers/media/video/ovcamchip/ov6x30.c b/drivers/media/video/ovcamchip/ov6x30.c deleted file mode 100644 index 73b94f51a85..00000000000 --- a/drivers/media/video/ovcamchip/ov6x30.c +++ /dev/null @@ -1,373 +0,0 @@ -/* OmniVision OV6630/OV6130 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_SAT 0x03 /* saturation [7:3] */ -#define REG_CNT 0x05 /* Y contrast [3:0] */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_SHARP 0x07 /* sharpness */ -#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ -#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ -#define REG_EXP 0x10 /* exposure */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3A -#define VWSBASE 0x05 -#define VWEBASE 0x06 - -struct ov6x30 { - int auto_brt; - int auto_exp; - int backlight; - int bandfilt; - int mirror; -}; - -static struct ovcamchip_regvals regvals_init_6x30[] = { - { 0x12, 0x80 }, /* reset */ - { 0x00, 0x1f }, /* Gain */ - { 0x01, 0x99 }, /* Blue gain */ - { 0x02, 0x7c }, /* Red gain */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x0a }, /* Contrast */ - { 0x06, 0x95 }, /* Brightness */ - { 0x07, 0x2d }, /* Sharpness */ - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x0e, 0x20 }, - { 0x0f, 0x05 }, - { 0x10, 0x9a }, /* "exposure check" */ - { 0x11, 0x00 }, /* Pixel clock = fastest */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x21 }, - { 0x14, 0x80 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x38 }, - { 0x18, 0xea }, - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, - { 0x1e, 0xc4 }, - { 0x1f, 0x04 }, - { 0x20, 0x20 }, - { 0x21, 0x10 }, - { 0x22, 0x88 }, - { 0x23, 0xc0 }, /* Crystal circuit power level */ - { 0x25, 0x9a }, /* Increase AEC black pixel ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x84 }, /* (keep) */ - { 0x2b, 0xa8 }, /* (keep) */ - { 0x2c, 0xa0 }, - { 0x2d, 0x95 }, /* Enable auto-brightness */ - { 0x2e, 0x88 }, - { 0x33, 0x26 }, - { 0x34, 0x03 }, - { 0x36, 0x8f }, - { 0x37, 0x80 }, - { 0x38, 0x83 }, - { 0x39, 0x80 }, - { 0x3a, 0x0f }, - { 0x3b, 0x3c }, - { 0x3c, 0x1a }, - { 0x3d, 0x80 }, - { 0x3e, 0x80 }, - { 0x3f, 0x0e }, - { 0x40, 0x00 }, /* White bal */ - { 0x41, 0x00 }, /* White bal */ - { 0x42, 0x80 }, - { 0x43, 0x3f }, /* White bal */ - { 0x44, 0x80 }, - { 0x45, 0x20 }, - { 0x46, 0x20 }, - { 0x47, 0x80 }, - { 0x48, 0x7f }, - { 0x49, 0x00 }, - { 0x4a, 0x00 }, - { 0x4b, 0x80 }, - { 0x4c, 0xd0 }, - { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ - { 0x4e, 0x40 }, - { 0x4f, 0x07 }, /* UV average mode, color killer: strongest */ - { 0x50, 0xff }, - { 0x54, 0x23 }, /* Max AGC gain: 18dB */ - { 0x55, 0xff }, - { 0x56, 0x12 }, - { 0x57, 0x81 }, /* (default) */ - { 0x58, 0x75 }, - { 0x59, 0x01 }, /* AGC dark current compensation: +1 */ - { 0x5a, 0x2c }, - { 0x5b, 0x0f }, /* AWB chrominance levels */ - { 0x5c, 0x10 }, - { 0x3d, 0x80 }, - { 0x27, 0xa6 }, - /* Toggle AWB off and on */ - { 0x12, 0x20 }, - { 0x12, 0x24 }, - - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV6x30 camera chip and relevant variables. */ -static int ov6x30_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x30 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - rc = ov_write_regvals(c, regvals_init_6x30); - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov6x30_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov6x30_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x30 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f); - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_HUE: - rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); - if (rc < 0) - goto out; - - rc = ov_write(c, REG_BLUE, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write(c, 0x2b, sixty?0xa8:0x28); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2a, sixty?0x84:0xa4); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_BACKLIGHT: - { - rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); - s->backlight = v; - break; - } - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov6x30_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x30 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, REG_CNT, &val); - ctl->value = (val & 0x0f) << 12; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_HUE: - rc = ov_read(c, REG_BLUE, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_BACKLIGHT: - ctl->value = s->backlight; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - /******** QCIF-specific regs ********/ - - ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20); - - /******** Palette-specific regs ********/ - - if (win->format == VIDEO_PALETTE_GREY) { - if (c->adapter->id == I2C_HW_SMBUS_OV518) { - /* Do nothing - we're already in 8-bit mode */ - } else { - ov_write_mask(c, 0x13, 0x20, 0x20); - } - } else { - /* The OV518 needs special treatment. Although both the OV518 - * and the OV6630 support a 16-bit video bus, only the 8 bit Y - * bus is actually used. The UV bus is tied to ground. - * Therefore, the OV6630 needs to be in 8-bit multiplexed - * output mode */ - - if (c->adapter->id == I2C_HW_SMBUS_OV518) { - /* Do nothing - we want to stay in 8-bit mode */ - /* Warning: Messing with reg 0x13 breaks OV518 color */ - } else { - ov_write_mask(c, 0x13, 0x00, 0x20); - } - } - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - return 0; -} - -static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov6x30_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 0; - vwscale = 0; - } else { - hwscale = 1; - vwscale = 1; /* The datasheet says 0; it's wrong */ - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov6x30_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov6x30_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov6x30_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov6x30_ops = { - .init = ov6x30_init, - .free = ov6x30_free, - .command = ov6x30_command, -}; diff --git a/drivers/media/video/ovcamchip/ov76be.c b/drivers/media/video/ovcamchip/ov76be.c deleted file mode 100644 index 11f6be924d8..00000000000 --- a/drivers/media/video/ovcamchip/ov76be.c +++ /dev/null @@ -1,302 +0,0 @@ -/* OmniVision OV76BE Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* OV7610 registers: Since the OV76BE is undocumented, we'll settle for these - * for now. */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue channel balance */ -#define REG_RED 0x02 /* red channel balance */ -#define REG_SAT 0x03 /* saturation */ -#define REG_CNT 0x05 /* Y contrast */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ -#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ -#define REG_GAMMA_COEFF 0x0E /* gamma settings */ -#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ -#define REG_EXP 0x10 /* manual exposure setting */ -#define REG_CLOCK 0x11 /* polarity/clock prescaler */ -#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ -#define REG_HWIN_START 0x17 /* horizontal window start */ -#define REG_HWIN_END 0x18 /* horizontal window end */ -#define REG_VWIN_START 0x19 /* vertical window start */ -#define REG_VWIN_END 0x1A /* vertical window end */ -#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ -#define REG_YOFFSET 0x21 /* Y channel offset */ -#define REG_UOFFSET 0x22 /* U channel offset */ -#define REG_ECW 0x24 /* exposure white level for AEC */ -#define REG_ECB 0x25 /* exposure black level for AEC */ -#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ -#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ -#define REG_ALC 0x2C /* Auto Level Control settings */ -#define REG_VOFFSET 0x2E /* V channel offset adjustment */ -#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ -#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ -#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3a -#define VWSBASE 0x05 -#define VWEBASE 0x05 - -struct ov76be { - int auto_brt; - int auto_exp; - int bandfilt; - int mirror; -}; - -/* NOTE: These are the same as the 7x10 settings, but should eventually be - * optimized for the OV76BE */ -static struct ovcamchip_regvals regvals_init_76be[] = { - { 0x10, 0xff }, - { 0x16, 0x03 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV76be camera chip and relevant variables. */ -static int ov76be_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov76be *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - rc = ov_write_regvals(c, regvals_init_76be); - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov76be_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov76be_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov76be *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2b, sixty?0x00:0xac); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x76, 0x01, 0x01); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov76be_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov76be *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov76be_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - int qvga = win->quarter; - - /******** QVGA-specific regs ********/ - - ov_write(c, 0x14, qvga?0xa4:0x84); - - /******** Palette-specific regs ********/ - - if (win->format == VIDEO_PALETTE_GREY) { - ov_write_mask(c, 0x0e, 0x40, 0x40); - ov_write_mask(c, 0x13, 0x20, 0x20); - } else { - ov_write_mask(c, 0x0e, 0x00, 0x40); - ov_write_mask(c, 0x13, 0x00, 0x20); - } - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - /******** Resolution-specific ********/ - - if (win->width == 640 && win->height == 480) - ov_write(c, 0x35, 0x9e); - else - ov_write(c, 0x35, 0x1e); - - return 0; -} - -static int ov76be_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov76be_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 1; - vwscale = 0; - } else { - hwscale = 2; - vwscale = 1; - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov76be_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov76be_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov76be_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov76be_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov76be_ops = { - .init = ov76be_init, - .free = ov76be_free, - .command = ov76be_command, -}; diff --git a/drivers/media/video/ovcamchip/ov7x10.c b/drivers/media/video/ovcamchip/ov7x10.c deleted file mode 100644 index 5206e791392..00000000000 --- a/drivers/media/video/ovcamchip/ov7x10.c +++ /dev/null @@ -1,334 +0,0 @@ -/* OmniVision OV7610/OV7110 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue channel balance */ -#define REG_RED 0x02 /* red channel balance */ -#define REG_SAT 0x03 /* saturation */ -#define REG_CNT 0x05 /* Y contrast */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ -#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ -#define REG_GAMMA_COEFF 0x0E /* gamma settings */ -#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ -#define REG_EXP 0x10 /* manual exposure setting */ -#define REG_CLOCK 0x11 /* polarity/clock prescaler */ -#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ -#define REG_HWIN_START 0x17 /* horizontal window start */ -#define REG_HWIN_END 0x18 /* horizontal window end */ -#define REG_VWIN_START 0x19 /* vertical window start */ -#define REG_VWIN_END 0x1A /* vertical window end */ -#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ -#define REG_YOFFSET 0x21 /* Y channel offset */ -#define REG_UOFFSET 0x22 /* U channel offset */ -#define REG_ECW 0x24 /* exposure white level for AEC */ -#define REG_ECB 0x25 /* exposure black level for AEC */ -#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ -#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ -#define REG_ALC 0x2C /* Auto Level Control settings */ -#define REG_VOFFSET 0x2E /* V channel offset adjustment */ -#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ -#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ -#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3a -#define VWSBASE 0x05 -#define VWEBASE 0x05 - -struct ov7x10 { - int auto_brt; - int auto_exp; - int bandfilt; - int mirror; -}; - -/* Lawrence Glaister <lg@jfm.bc.ca> reports: - * - * Register 0x0f in the 7610 has the following effects: - * - * 0x85 (AEC method 1): Best overall, good contrast range - * 0x45 (AEC method 2): Very overexposed - * 0xa5 (spec sheet default): Ok, but the black level is - * shifted resulting in loss of contrast - * 0x05 (old driver setting): very overexposed, too much - * contrast - */ -static struct ovcamchip_regvals regvals_init_7x10[] = { - { 0x10, 0xff }, - { 0x16, 0x03 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV7x10 camera chip and relevant variables. */ -static int ov7x10_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x10 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - rc = ov_write_regvals(c, regvals_init_7x10); - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov7x10_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov7x10_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x10 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_write(c, REG_CNT, v >> 8); - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_HUE: - rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); - if (rc < 0) - goto out; - - rc = ov_write(c, REG_BLUE, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2b, sixty?0x00:0xac); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x13, 0x10, 0x10); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x13, 0x00, 0x10); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov7x10_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x10 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, REG_CNT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_HUE: - rc = ov_read(c, REG_BLUE, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - int qvga = win->quarter; - - /******** QVGA-specific regs ********/ - - ov_write(c, 0x14, qvga?0x24:0x04); - - /******** Palette-specific regs ********/ - - if (win->format == VIDEO_PALETTE_GREY) { - ov_write_mask(c, 0x0e, 0x40, 0x40); - ov_write_mask(c, 0x13, 0x20, 0x20); - } else { - ov_write_mask(c, 0x0e, 0x00, 0x40); - ov_write_mask(c, 0x13, 0x00, 0x20); - } - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - /******** Resolution-specific ********/ - - if (win->width == 640 && win->height == 480) - ov_write(c, 0x35, 0x9e); - else - ov_write(c, 0x35, 0x1e); - - return 0; -} - -static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov7x10_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 1; - vwscale = 0; - } else { - hwscale = 2; - vwscale = 1; - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov7x10_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov7x10_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov7x10_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov7x10_ops = { - .init = ov7x10_init, - .free = ov7x10_free, - .command = ov7x10_command, -}; diff --git a/drivers/media/video/ovcamchip/ov7x20.c b/drivers/media/video/ovcamchip/ov7x20.c deleted file mode 100644 index 8e26ae338f3..00000000000 --- a/drivers/media/video/ovcamchip/ov7x20.c +++ /dev/null @@ -1,454 +0,0 @@ -/* OmniVision OV7620/OV7120 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_SAT 0x03 /* saturation */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_SHARP 0x07 /* analog sharpness */ -#define REG_BLUE_BIAS 0x0C /* WB blue ratio [5:0] */ -#define REG_RED_BIAS 0x0D /* WB red ratio [5:0] */ -#define REG_EXP 0x10 /* exposure */ - -/* Default control settings. Values are in terms of V4L2 controls. */ -#define OV7120_DFL_BRIGHT 0x60 -#define OV7620_DFL_BRIGHT 0x60 -#define OV7120_DFL_SAT 0xb0 -#define OV7620_DFL_SAT 0xc0 -#define DFL_AUTO_EXP 1 -#define DFL_AUTO_GAIN 1 -#define OV7120_DFL_GAIN 0x00 -#define OV7620_DFL_GAIN 0x00 -/* NOTE: Since autoexposure is the default, these aren't programmed into the - * OV7x20 chip. They are just here because V4L2 expects a default */ -#define OV7120_DFL_EXP 0x7f -#define OV7620_DFL_EXP 0x7f - -/* Window parameters */ -#define HWSBASE 0x2F /* From 7620.SET (spec is wrong) */ -#define HWEBASE 0x2F -#define VWSBASE 0x05 -#define VWEBASE 0x05 - -struct ov7x20 { - int auto_brt; - int auto_exp; - int auto_gain; - int backlight; - int bandfilt; - int mirror; -}; - -/* Contrast look-up table */ -static unsigned char ctab[] = { - 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, - 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff -}; - -/* Settings for (Black & White) OV7120 camera chip */ -static struct ovcamchip_regvals regvals_init_7120[] = { - { 0x12, 0x80 }, /* reset */ - { 0x13, 0x00 }, /* Autoadjust off */ - { 0x12, 0x20 }, /* Disable AWB */ - { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, /* Autoadjust on (if desired) */ - { 0x00, OV7120_DFL_GAIN }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, OV7120_DFL_SAT }, - { 0x06, OV7120_DFL_BRIGHT }, - { 0x07, 0x00 }, - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x11, 0x01 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x08 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa0 }, - { 0x27, 0xfa }, - { 0x28, 0x20 }, /* DON'T set bit 6. It is for the OV7620 only */ - { 0x29, DFL_AUTO_EXP?0x00:0x80 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x95 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x20 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, /* OV says "don't change this value" */ - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0xe2 }, - { 0x6c, 0x40 }, - { 0x6d, 0x48 }, - { 0x6e, 0x80 }, - { 0x6f, 0x0d }, - { 0x70, 0x89 }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0xa0 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe6 }, - { 0x7c, 0x00 }, - { 0x24, 0x3a }, - { 0x25, 0x60 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* Settings for (color) OV7620 camera chip */ -static struct ovcamchip_regvals regvals_init_7620[] = { - { 0x12, 0x80 }, /* reset */ - { 0x00, OV7620_DFL_GAIN }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, OV7620_DFL_SAT }, - { 0x06, OV7620_DFL_BRIGHT }, - { 0x07, 0x00 }, - { 0x0c, 0x24 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x11, 0x01 }, - { 0x12, 0x24 }, - { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x18 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x20 }, - { 0x29, DFL_AUTO_EXP?0x00:0x80 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x91 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x27 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x02 }, - { 0x6d, 0x44 }, - { 0x6e, 0x80 }, - { 0x6f, 0x1d }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe2 }, - { 0x7c, 0x00 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* Returns index into the specified look-up table, with 'n' elements, for which - * the value is greater than or equal to "val". If a match isn't found, (n-1) - * is returned. The entries in the table must be in ascending order. */ -static inline int ov7x20_lut_find(unsigned char lut[], int n, unsigned char val) -{ - int i = 0; - - while (lut[i] < val && i < n) - i++; - - return i; -} - -/* This initializes the OV7x20 camera chip and relevant variables. */ -static int ov7x20_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x20 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - if (ov->mono) - rc = ov_write_regvals(c, regvals_init_7120); - else - rc = ov_write_regvals(c, regvals_init_7620); - - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = DFL_AUTO_EXP; - s->auto_gain = DFL_AUTO_GAIN; - - return 0; -} - -static int ov7x20_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov7x20_set_v4l1_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x20 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - { - /* Use Y gamma control instead. Bit 0 enables it. */ - rc = ov_write(c, 0x64, ctab[v >> 12]); - break; - } - case OVCAMCHIP_CID_BRIGHT: - /* 7620 doesn't like manual changes when in auto mode */ - if (!s->auto_brt) - rc = ov_write(c, REG_BRT, v >> 8); - else - rc = 0; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - if (!s->auto_exp) - rc = ov_write(c, REG_EXP, v); - else - rc = -EBUSY; - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2b, sixty?0x00:0xac); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x76, 0x01, 0x01); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_BACKLIGHT: - { - rc = ov_write_mask(c, 0x68, v?0xe0:0xc0, 0xe0); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); - s->backlight = v; - break; - } - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov7x20_get_v4l1_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x20 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, 0x64, &val); - ctl->value = ov7x20_lut_find(ctab, 16, val) << 12; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_BACKLIGHT: - ctl->value = s->backlight; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov7x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int qvga = win->quarter; - - /******** QVGA-specific regs ********/ - ov_write_mask(c, 0x14, qvga?0x20:0x00, 0x20); - ov_write_mask(c, 0x28, qvga?0x00:0x20, 0x20); - ov_write(c, 0x24, qvga?0x20:0x3a); - ov_write(c, 0x25, qvga?0x30:0x60); - ov_write_mask(c, 0x2d, qvga?0x40:0x00, 0x40); - if (!ov->mono) - ov_write_mask(c, 0x67, qvga?0xf0:0x90, 0xf0); - ov_write_mask(c, 0x74, qvga?0x20:0x00, 0x20); - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - return 0; -} - -static int ov7x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov7x20_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 1; - vwscale = 0; - } else { - hwscale = 2; - vwscale = 1; - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov7x20_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov7x20_set_v4l1_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov7x20_get_v4l1_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov7x20_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov7x20_ops = { - .init = ov7x20_init, - .free = ov7x20_free, - .command = ov7x20_command, -}; diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c deleted file mode 100644 index d573d842899..00000000000 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ /dev/null @@ -1,395 +0,0 @@ -/* Shared Code for OmniVision Camera Chip Drivers - * - * Copyright (c) 2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv.h> -#include "ovcamchip_priv.h" - -#define DRIVER_VERSION "v2.27 for Linux 2.6" -#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org>" -#define DRIVER_DESC "OV camera chip I2C driver" - -#define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args); -#define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args); - -#ifdef DEBUG -int ovcamchip_debug = 0; -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, - "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all"); -#endif - -/* By default, let bridge driver tell us if chip is monochrome. mono=0 - * will ignore that and always treat chips as color. mono=1 will force - * monochrome mode for all chips. */ -static int mono = -1; -module_param(mono, int, 0); -MODULE_PARM_DESC(mono, - "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)"); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - - -/* Registers common to all chips, that are needed for detection */ -#define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ -#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ -#define GENERIC_REG_COM_I 0x29 /* misc ID bits */ - -static char *chip_names[NUM_CC_TYPES] = { - [CC_UNKNOWN] = "Unknown chip", - [CC_OV76BE] = "OV76BE", - [CC_OV7610] = "OV7610", - [CC_OV7620] = "OV7620", - [CC_OV7620AE] = "OV7620AE", - [CC_OV6620] = "OV6620", - [CC_OV6630] = "OV6630", - [CC_OV6630AE] = "OV6630AE", - [CC_OV6630AF] = "OV6630AF", -}; - -/* ----------------------------------------------------------------------- */ - -int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) -{ - int rc; - - while (rvals->reg != 0xff) { - rc = ov_write(c, rvals->reg, rvals->val); - if (rc < 0) - return rc; - rvals++; - } - - return 0; -} - -/* Writes bits at positions specified by mask to an I2C reg. Bits that are in - * the same position as 1's in "mask" are cleared and set to "value". Bits - * that are in the same position as 0's in "mask" are preserved, regardless - * of their respective state in "value". - */ -int ov_write_mask(struct i2c_client *c, - unsigned char reg, - unsigned char value, - unsigned char mask) -{ - int rc; - unsigned char oldval, newval; - - if (mask == 0xff) { - newval = value; - } else { - rc = ov_read(c, reg, &oldval); - if (rc < 0) - return rc; - - oldval &= (~mask); /* Clear the masked bits */ - value &= mask; /* Enforce mask on value */ - newval = oldval | value; /* Set the desired bits */ - } - - return ov_write(c, reg, newval); -} - -/* ----------------------------------------------------------------------- */ - -/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure. - */ -static int init_camchip(struct i2c_client *c) -{ - int i, success; - unsigned char high, low; - - /* Reset the chip */ - ov_write(c, 0x12, 0x80); - - /* Wait for it to initialize */ - msleep(150); - - for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { - if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { - if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) { - if (high == 0x7F && low == 0xA2) { - success = 1; - continue; - } - } - } - - /* Reset the chip */ - ov_write(c, 0x12, 0x80); - - /* Wait for it to initialize */ - msleep(150); - - /* Dummy read to sync I2C */ - ov_read(c, 0x00, &low); - } - - if (!success) - return -EIO; - - PDEBUG(1, "I2C synced in %d attempt(s)", i); - - return 0; -} - -/* This detects the OV7610, OV7620, or OV76BE chip. */ -static int ov7xx0_detect(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int rc; - unsigned char val; - - PDEBUG(4, ""); - - /* Detect chip (sub)type */ - rc = ov_read(c, GENERIC_REG_COM_I, &val); - if (rc < 0) { - PERROR("Error detecting ov7xx0 type"); - return rc; - } - - if ((val & 3) == 3) { - PINFO("Camera chip is an OV7610"); - ov->subtype = CC_OV7610; - } else if ((val & 3) == 1) { - rc = ov_read(c, 0x15, &val); - if (rc < 0) { - PERROR("Error detecting ov7xx0 type"); - return rc; - } - - if (val & 1) { - PINFO("Camera chip is an OV7620AE"); - /* OV7620 is a close enough match for now. There are - * some definite differences though, so this should be - * fixed */ - ov->subtype = CC_OV7620; - } else { - PINFO("Camera chip is an OV76BE"); - ov->subtype = CC_OV76BE; - } - } else if ((val & 3) == 0) { - PINFO("Camera chip is an OV7620"); - ov->subtype = CC_OV7620; - } else { - PERROR("Unknown camera chip version: %d", val & 3); - return -ENOSYS; - } - - if (ov->subtype == CC_OV76BE) - ov->sops = &ov76be_ops; - else if (ov->subtype == CC_OV7620) - ov->sops = &ov7x20_ops; - else - ov->sops = &ov7x10_ops; - - return 0; -} - -/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */ -static int ov6xx0_detect(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int rc; - unsigned char val; - - PDEBUG(4, ""); - - /* Detect chip (sub)type */ - rc = ov_read(c, GENERIC_REG_COM_I, &val); - if (rc < 0) { - PERROR("Error detecting ov6xx0 type"); - return -1; - } - - if ((val & 3) == 0) { - ov->subtype = CC_OV6630; - PINFO("Camera chip is an OV6630"); - } else if ((val & 3) == 1) { - ov->subtype = CC_OV6620; - PINFO("Camera chip is an OV6620"); - } else if ((val & 3) == 2) { - ov->subtype = CC_OV6630; - PINFO("Camera chip is an OV6630AE"); - } else if ((val & 3) == 3) { - ov->subtype = CC_OV6630; - PINFO("Camera chip is an OV6630AF"); - } - - if (ov->subtype == CC_OV6620) - ov->sops = &ov6x20_ops; - else - ov->sops = &ov6x30_ops; - - return 0; -} - -static int ovcamchip_detect(struct i2c_client *c) -{ - /* Ideally we would just try a single register write and see if it NAKs. - * That isn't possible since the OV518 can't report I2C transaction - * failures. So, we have to try to initialize the chip (i.e. reset it - * and check the ID registers) to detect its presence. */ - - /* Test for 7xx0 */ - PDEBUG(3, "Testing for 0V7xx0"); - if (init_camchip(c) < 0) - return -ENODEV; - /* 7-bit addresses with bit 0 set are for the OV7xx0 */ - if (c->addr & 1) { - if (ov7xx0_detect(c) < 0) { - PERROR("Failed to init OV7xx0"); - return -EIO; - } - return 0; - } - /* Test for 6xx0 */ - PDEBUG(3, "Testing for 0V6xx0"); - if (ov6xx0_detect(c) < 0) { - PERROR("Failed to init OV6xx0"); - return -EIO; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static long ovcamchip_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct ovcamchip *ov = to_ovcamchip(sd); - struct i2c_client *c = v4l2_get_subdevdata(sd); - - if (!ov->initialized && - cmd != OVCAMCHIP_CMD_Q_SUBTYPE && - cmd != OVCAMCHIP_CMD_INITIALIZE) { - v4l2_err(sd, "Camera chip not initialized yet!\n"); - return -EPERM; - } - - switch (cmd) { - case OVCAMCHIP_CMD_Q_SUBTYPE: - { - *(int *)arg = ov->subtype; - return 0; - } - case OVCAMCHIP_CMD_INITIALIZE: - { - int rc; - - if (mono == -1) - ov->mono = *(int *)arg; - else - ov->mono = mono; - - if (ov->mono) { - if (ov->subtype != CC_OV7620) - v4l2_warn(sd, "Monochrome not " - "implemented for this chip\n"); - else - v4l2_info(sd, "Initializing chip as " - "monochrome\n"); - } - - rc = ov->sops->init(c); - if (rc < 0) - return rc; - - ov->initialized = 1; - return 0; - } - default: - return ov->sops->command(c, cmd, arg); - } -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops ovcamchip_core_ops = { - .ioctl = ovcamchip_ioctl, -}; - -static const struct v4l2_subdev_ops ovcamchip_ops = { - .core = &ovcamchip_core_ops, -}; - -static int ovcamchip_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ovcamchip *ov; - struct v4l2_subdev *sd; - int rc = 0; - - ov = kzalloc(sizeof *ov, GFP_KERNEL); - if (!ov) { - rc = -ENOMEM; - goto no_ov; - } - sd = &ov->sd; - v4l2_i2c_subdev_init(sd, client, &ovcamchip_ops); - - rc = ovcamchip_detect(client); - if (rc < 0) - goto error; - - v4l_info(client, "%s found @ 0x%02x (%s)\n", - chip_names[ov->subtype], client->addr << 1, client->adapter->name); - - PDEBUG(1, "Camera chip detection complete"); - - return rc; -error: - kfree(ov); -no_ov: - PDEBUG(1, "returning %d", rc); - return rc; -} - -static int ovcamchip_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ovcamchip *ov = to_ovcamchip(sd); - int rc; - - v4l2_device_unregister_subdev(sd); - rc = ov->sops->free(client); - if (rc < 0) - return rc; - - kfree(ov); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id ovcamchip_id[] = { - { "ovcamchip", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ovcamchip_id); - -static struct v4l2_i2c_driver_data v4l2_i2c_data = { - .name = "ovcamchip", - .probe = ovcamchip_probe, - .remove = ovcamchip_remove, - .id_table = ovcamchip_id, -}; diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h deleted file mode 100644 index 4f07b78c88b..00000000000 --- a/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ /dev/null @@ -1,101 +0,0 @@ -/* OmniVision* camera chip driver private definitions for core code and - * chip-specific code - * - * Copyright (c) 1999-2004 Mark McClelland - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - * - * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver - * is not sponsored or developed by them. - */ - -#ifndef __LINUX_OVCAMCHIP_PRIV_H -#define __LINUX_OVCAMCHIP_PRIV_H - -#include <linux/i2c.h> -#include <media/v4l2-subdev.h> -#include <media/ovcamchip.h> - -#ifdef DEBUG -extern int ovcamchip_debug; -#endif - -#define PDEBUG(level, fmt, args...) \ - if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args) - -#define DDEBUG(level, dev, fmt, args...) \ - if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args) - -/* Number of times to retry chip detection. Increase this if you are getting - * "Failed to init camera chip" */ -#define I2C_DETECT_RETRIES 10 - -struct ovcamchip_regvals { - unsigned char reg; - unsigned char val; -}; - -struct ovcamchip_ops { - int (*init)(struct i2c_client *); - int (*free)(struct i2c_client *); - int (*command)(struct i2c_client *, unsigned int, void *); -}; - -struct ovcamchip { - struct v4l2_subdev sd; - struct ovcamchip_ops *sops; - void *spriv; /* Private data for OV7x10.c etc... */ - int subtype; /* = SEN_OV7610 etc... */ - int mono; /* Monochrome chip? (invalid until init) */ - int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ -}; - -static inline struct ovcamchip *to_ovcamchip(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ovcamchip, sd); -} - -extern struct ovcamchip_ops ov6x20_ops; -extern struct ovcamchip_ops ov6x30_ops; -extern struct ovcamchip_ops ov7x10_ops; -extern struct ovcamchip_ops ov7x20_ops; -extern struct ovcamchip_ops ov76be_ops; - -/* --------------------------------- */ -/* I2C I/O */ -/* --------------------------------- */ - -static inline int ov_read(struct i2c_client *c, unsigned char reg, - unsigned char *value) -{ - int rc; - - rc = i2c_smbus_read_byte_data(c, reg); - *value = (unsigned char) rc; - return rc; -} - -static inline int ov_write(struct i2c_client *c, unsigned char reg, - unsigned char value ) -{ - return i2c_smbus_write_byte_data(c, reg, value); -} - -/* --------------------------------- */ -/* FUNCTION PROTOTYPES */ -/* --------------------------------- */ - -/* Functions in ovcamchip_core.c */ - -extern int ov_write_regvals(struct i2c_client *c, - struct ovcamchip_regvals *rvals); - -extern int ov_write_mask(struct i2c_client *c, unsigned char reg, - unsigned char value, unsigned char mask); - -#endif diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index e9b11e119f6..4279ebb811a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -94,8 +94,6 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, u32 *num_ptr) { u32 result = 0; - u32 val; - int ch; int radix = 10; if ((count >= 2) && (buf[0] == '0') && ((buf[1] == 'x') || (buf[1] == 'X'))) { @@ -107,17 +105,9 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, } while (count--) { - ch = *buf++; - if ((ch >= '0') && (ch <= '9')) { - val = ch - '0'; - } else if ((ch >= 'a') && (ch <= 'f')) { - val = ch - 'a' + 10; - } else if ((ch >= 'A') && (ch <= 'F')) { - val = ch - 'A' + 10; - } else { + int val = hex_to_bin(*buf++); + if (val < 0 || val >= radix) return -EINVAL; - } - if (val >= radix) return -EINVAL; result *= radix; result += val; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index b4824782d85..bba6115c9ae 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -223,7 +223,10 @@ int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) " pvr2_ioread_setup (setup) id=%p",cp); pvr2_stream_kill(sp); ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); - if (ret < 0) return ret; + if (ret < 0) { + mutex_unlock(&cp->mutex); + return ret; + } for (idx = 0; idx < BUFFER_COUNT; idx++) { bp = pvr2_stream_get_buffer(sp,idx); pvr2_buffer_set_buffer(bp, diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index fb242f6cfb1..9de7d59916b 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -276,7 +276,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) * longer in STATE_QUEUED or STATE_ACTIVE */ videobuf_waiton(&buf->vb, 0, 0); - videobuf_dma_unmap(vq, dma); + videobuf_dma_unmap(vq->dev, dma); videobuf_dma_free(dma); for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { @@ -1284,7 +1284,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id } switch (code) { - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: formats++; if (xlate) { xlate->host_fmt = &pxa_camera_formats[0]; @@ -1293,9 +1293,9 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id dev_dbg(dev, "Providing format %s using code %d\n", pxa_camera_formats[0].name, code); } - case V4L2_MBUS_FMT_YVYU8_2X8_BE: - case V4L2_MBUS_FMT_YUYV8_2X8_LE: - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_VYUY8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YVYU8_2X8: case V4L2_MBUS_FMT_RGB565_2X8_LE: case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: if (xlate) diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 47fd207ba3b..ce78fff2342 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -1,5 +1,5 @@ /* - * Driver for RJ54N1CB0C CMOS Image Sensor from Micron + * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp * * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> * @@ -127,8 +127,8 @@ static const struct rj54n1_datafmt *rj54n1_find_datafmt( } static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { - {V4L2_MBUS_FMT_YUYV8_2X8_LE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YVYU8_2X8_LE, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, @@ -1046,12 +1046,12 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ switch (mf->code) { - case V4L2_MBUS_FMT_YUYV8_2X8_LE: + case V4L2_MBUS_FMT_YUYV8_2X8: ret = reg_write(client, RJ54N1_OUT_SEL, 0); if (!ret) ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); break; - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_YVYU8_2X8: ret = reg_write(client, RJ54N1_OUT_SEL, 0); if (!ret) ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 3c7a79f3812..8ec7c9a45a1 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -191,7 +191,6 @@ struct s2255_bufferi { struct s2255_dmaqueue { struct list_head active; struct s2255_dev *dev; - int channel; }; /* for firmware loading, fw_state */ @@ -226,51 +225,60 @@ struct s2255_pipeinfo { }; struct s2255_fmt; /*forward declaration */ +struct s2255_dev; + +struct s2255_channel { + struct video_device vdev; + int resources; + struct s2255_dmaqueue vidq; + struct s2255_bufferi buffer; + struct s2255_mode mode; + /* jpeg compression */ + struct v4l2_jpegcompression jc; + /* capture parameters (for high quality mode full size) */ + struct v4l2_captureparm cap_parm; + int cur_frame; + int last_frame; + + int b_acquire; + /* allocated image size */ + unsigned long req_image_size; + /* received packet size */ + unsigned long pkt_size; + int bad_payload; + unsigned long frame_count; + /* if JPEG image */ + int jpg_size; + /* if channel configured to default state */ + int configured; + wait_queue_head_t wait_setmode; + int setmode_ready; + /* video status items */ + int vidstatus; + wait_queue_head_t wait_vidstatus; + int vidstatus_ready; + unsigned int width; + unsigned int height; + const struct s2255_fmt *fmt; + int idx; /* channel number on device, 0-3 */ +}; + struct s2255_dev { - struct video_device vdev[MAX_CHANNELS]; + struct s2255_channel channel[MAX_CHANNELS]; struct v4l2_device v4l2_dev; - atomic_t channels; /* number of channels registered */ + atomic_t num_channels; int frames; struct mutex lock; struct mutex open_lock; - int resources[MAX_CHANNELS]; struct usb_device *udev; struct usb_interface *interface; u8 read_endpoint; - - struct s2255_dmaqueue vidq[MAX_CHANNELS]; struct timer_list timer; struct s2255_fw *fw_data; struct s2255_pipeinfo pipe; - struct s2255_bufferi buffer[MAX_CHANNELS]; - struct s2255_mode mode[MAX_CHANNELS]; - /* jpeg compression */ - struct v4l2_jpegcompression jc[MAX_CHANNELS]; - /* capture parameters (for high quality mode full size) */ - struct v4l2_captureparm cap_parm[MAX_CHANNELS]; - const struct s2255_fmt *cur_fmt[MAX_CHANNELS]; - int cur_frame[MAX_CHANNELS]; - int last_frame[MAX_CHANNELS]; u32 cc; /* current channel */ - int b_acquire[MAX_CHANNELS]; - /* allocated image size */ - unsigned long req_image_size[MAX_CHANNELS]; - /* received packet size */ - unsigned long pkt_size[MAX_CHANNELS]; - int bad_payload[MAX_CHANNELS]; - unsigned long frame_count[MAX_CHANNELS]; int frame_ready; - /* if JPEG image */ - int jpg_size[MAX_CHANNELS]; - /* if channel configured to default state */ - int chn_configured[MAX_CHANNELS]; - wait_queue_head_t wait_setmode[MAX_CHANNELS]; - int setmode_ready[MAX_CHANNELS]; - /* video status items */ - int vidstatus[MAX_CHANNELS]; - wait_queue_head_t wait_vidstatus[MAX_CHANNELS]; - int vidstatus_ready[MAX_CHANNELS]; int chn_ready; spinlock_t slock; /* dsp firmware version (f2255usb.bin) */ @@ -298,16 +306,10 @@ struct s2255_buffer { struct s2255_fh { struct s2255_dev *dev; - const struct s2255_fmt *fmt; - unsigned int width; - unsigned int height; struct videobuf_queue vb_vidq; enum v4l2_buf_type type; - int channel; - /* mode below is the desired mode. - mode in s2255_dev is the current mode that was last set */ - struct s2255_mode mode; - int resources[MAX_CHANNELS]; + struct s2255_channel *channel; + int resources; }; /* current cypress EEPROM firmware version */ @@ -360,12 +362,11 @@ static int *s2255_debug = &debug; static int s2255_start_readpipe(struct s2255_dev *dev); static void s2255_stop_readpipe(struct s2255_dev *dev); -static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn); -static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn); -static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn, int jpgsize); -static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, - struct s2255_mode *mode); +static int s2255_start_acquire(struct s2255_channel *channel); +static int s2255_stop_acquire(struct s2255_channel *channel); +static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf, + int jpgsize); +static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode); static int s2255_board_shutdown(struct s2255_dev *dev); static void s2255_fwload_start(struct s2255_dev *dev, int reset); static void s2255_destroy(struct s2255_dev *dev); @@ -577,10 +578,11 @@ static void s2255_fwchunk_complete(struct urb *urb) } -static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) +static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) { - struct s2255_dmaqueue *dma_q = &dev->vidq[chn]; + struct s2255_dmaqueue *dma_q = &channel->vidq; struct s2255_buffer *buf; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); unsigned long flags = 0; int rc = 0; spin_lock_irqsave(&dev->slock, flags); @@ -593,7 +595,7 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) struct s2255_buffer, vb.queue); list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); - s2255_fillbuff(dev, buf, dma_q->channel, jpgsize); + s2255_fillbuff(channel, buf, jpgsize); wake_up(&buf->vb.done); dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); unlock: @@ -621,8 +623,8 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) * http://v4l.videotechnology.com/ * */ -static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn, int jpgsize) +static void s2255_fillbuff(struct s2255_channel *channel, + struct s2255_buffer *buf, int jpgsize) { int pos = 0; struct timeval ts; @@ -633,12 +635,11 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, if (!vbuf) return; - - last_frame = dev->last_frame[chn]; + last_frame = channel->last_frame; if (last_frame != -1) { - frm = &dev->buffer[chn].frame[last_frame]; + frm = &channel->buffer.frame[last_frame]; tmpbuf = - (const char *)dev->buffer[chn].frame[last_frame].lpvbits; + (const char *)channel->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: @@ -661,7 +662,7 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, default: printk(KERN_DEBUG "s2255: unknown format?\n"); } - dev->last_frame[chn] = -1; + channel->last_frame = -1; } else { printk(KERN_ERR "s2255: =======no frame\n"); return; @@ -671,7 +672,7 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, (unsigned long)vbuf, pos); /* tell v4l buffer was filled */ - buf->vb.field_count = dev->frame_count[chn] * 2; + buf->vb.field_count = channel->frame_count * 2; do_gettimeofday(&ts); buf->vb.ts = ts; buf->vb.state = VIDEOBUF_DONE; @@ -686,8 +687,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct s2255_fh *fh = vq->priv_data; - - *size = fh->width * fh->height * (fh->fmt->depth >> 3); + struct s2255_channel *channel = fh->channel; + *size = channel->width * channel->height * (channel->fmt->depth >> 3); if (0 == *count) *count = S2255_DEF_BUFS; @@ -710,30 +711,31 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); int rc; + int w = channel->width; + int h = channel->height; dprintk(4, "%s, field=%d\n", __func__, field); - if (fh->fmt == NULL) + if (channel->fmt == NULL) return -EINVAL; - if ((fh->width < norm_minw(&fh->dev->vdev[fh->channel])) || - (fh->width > norm_maxw(&fh->dev->vdev[fh->channel])) || - (fh->height < norm_minh(&fh->dev->vdev[fh->channel])) || - (fh->height > norm_maxh(&fh->dev->vdev[fh->channel]))) { + if ((w < norm_minw(&channel->vdev)) || + (w > norm_maxw(&channel->vdev)) || + (h < norm_minh(&channel->vdev)) || + (h > norm_maxh(&channel->vdev))) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } - - buf->vb.size = fh->width * fh->height * (fh->fmt->depth >> 3); - + buf->vb.size = w * h * (channel->fmt->depth >> 3); if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; + buf->fmt = channel->fmt; + buf->vb.width = w; + buf->vb.height = h; buf->vb.field = field; if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { @@ -753,8 +755,8 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); struct s2255_fh *fh = vq->priv_data; - struct s2255_dev *dev = fh->dev; - struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel]; + struct s2255_channel *channel = fh->channel; + struct s2255_dmaqueue *vidq = &channel->vidq; dprintk(1, "%s\n", __func__); buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active); @@ -765,7 +767,7 @@ static void buffer_release(struct videobuf_queue *vq, { struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); struct s2255_fh *fh = vq->priv_data; - dprintk(4, "%s %d\n", __func__, fh->channel); + dprintk(4, "%s %d\n", __func__, fh->channel->idx); free_buffer(vq, buf); } @@ -777,39 +779,43 @@ static struct videobuf_queue_ops s2255_video_qops = { }; -static int res_get(struct s2255_dev *dev, struct s2255_fh *fh) +static int res_get(struct s2255_fh *fh) { + struct s2255_dev *dev = fh->dev; /* is it free? */ + struct s2255_channel *channel = fh->channel; mutex_lock(&dev->lock); - if (dev->resources[fh->channel]) { + if (channel->resources) { /* no, someone else uses it */ mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ - dev->resources[fh->channel] = 1; - fh->resources[fh->channel] = 1; + channel->resources = 1; + fh->resources = 1; dprintk(1, "s2255: res: get\n"); mutex_unlock(&dev->lock); return 1; } -static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh) +static int res_locked(struct s2255_fh *fh) { - return dev->resources[fh->channel]; + return fh->channel->resources; } static int res_check(struct s2255_fh *fh) { - return fh->resources[fh->channel]; + return fh->resources; } -static void res_free(struct s2255_dev *dev, struct s2255_fh *fh) +static void res_free(struct s2255_fh *fh) { + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = fh->dev; mutex_lock(&dev->lock); - dev->resources[fh->channel] = 0; - fh->resources[fh->channel] = 0; + channel->resources = 0; + fh->resources = 0; mutex_unlock(&dev->lock); dprintk(1, "res: put\n"); } @@ -869,12 +875,13 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = f->fmt.pix.width * (fh->fmt->depth >> 3); + f->fmt.pix.pixelformat = channel->fmt->fourcc; + f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3); f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; } @@ -886,11 +893,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, enum v4l2_field field; int b_any_field = 0; struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; int is_ntsc; - is_ntsc = - (dev->vdev[fh->channel].current_norm & V4L2_STD_NTSC) ? 1 : 0; + (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -982,8 +988,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; const struct s2255_fmt *fmt; struct videobuf_queue *q = &fh->vb_vidq; + struct s2255_mode mode; int ret; int norm; @@ -1005,54 +1013,61 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out_s_fmt; } - if (res_locked(fh->dev, fh)) { + if (res_locked(fh)) { dprintk(1, "%s: channel busy\n", __func__); ret = -EBUSY; goto out_s_fmt; } - - fh->fmt = fmt; - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; + mode = channel->mode; + channel->fmt = fmt; + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - norm = norm_minw(&fh->dev->vdev[fh->channel]); - if (fh->width > norm_minw(&fh->dev->vdev[fh->channel])) { - if (fh->height > norm_minh(&fh->dev->vdev[fh->channel])) { - if (fh->dev->cap_parm[fh->channel].capturemode & + norm = norm_minw(&channel->vdev); + if (channel->width > norm_minw(&channel->vdev)) { + if (channel->height > norm_minh(&channel->vdev)) { + if (channel->cap_parm.capturemode & V4L2_MODE_HIGHQUALITY) - fh->mode.scale = SCALE_4CIFSI; + mode.scale = SCALE_4CIFSI; else - fh->mode.scale = SCALE_4CIFS; + mode.scale = SCALE_4CIFS; } else - fh->mode.scale = SCALE_2CIFS; + mode.scale = SCALE_2CIFS; } else { - fh->mode.scale = SCALE_1CIFS; + mode.scale = SCALE_1CIFS; } - /* color mode */ - switch (fh->fmt->fourcc) { + switch (channel->fmt->fourcc) { case V4L2_PIX_FMT_GREY: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_Y8; + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_Y8; break; case V4L2_PIX_FMT_JPEG: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_JPG; - fh->mode.color |= (fh->dev->jc[fh->channel].quality << 8); + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_JPG; + mode.color |= (channel->jc.quality << 8); break; case V4L2_PIX_FMT_YUV422P: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_YUVPL; + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPL; break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: default: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_YUVPK; + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPK; break; } + if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR)) + mode.restart = 1; + else if (mode.scale != channel->mode.scale) + mode.restart = 1; + else if (mode.format != channel->mode.format) + mode.restart = 1; + channel->mode = mode; + (void) s2255_set_mode(channel, &mode); ret = 0; out_s_fmt: mutex_unlock(&q->vb_lock); @@ -1197,26 +1212,27 @@ static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) * When the restart parameter is set, we sleep for ONE frame to allow the * DSP time to get the new frame */ -static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, +static int s2255_set_mode(struct s2255_channel *channel, struct s2255_mode *mode) { int res; __le32 *buffer; unsigned long chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); mutex_lock(&dev->lock); - chn_rev = G_chnmap[chn]; - dprintk(3, "%s channel %lu\n", __func__, chn); + chn_rev = G_chnmap[channel->idx]; + dprintk(3, "%s channel: %d\n", __func__, channel->idx); /* if JPEG, set the quality */ if ((mode->color & MASK_COLOR) == COLOR_JPG) { mode->color &= ~MASK_COLOR; mode->color |= COLOR_JPG; mode->color &= ~MASK_JPG_QUALITY; - mode->color |= (dev->jc[chn].quality << 8); + mode->color |= (channel->jc.quality << 8); } /* save the mode */ - dev->mode[chn] = *mode; - dev->req_image_size[chn] = get_transfer_size(mode); - dprintk(1, "%s: reqsize %ld\n", __func__, dev->req_image_size[chn]); + channel->mode = *mode; + channel->req_image_size = get_transfer_size(mode); + dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size); buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -1227,38 +1243,38 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, buffer[0] = IN_DATA_TOKEN; buffer[1] = (__le32) cpu_to_le32(chn_rev); buffer[2] = CMD_SET_MODE; - memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode)); - dev->setmode_ready[chn] = 0; + memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); + channel->setmode_ready = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (debug) s2255_print_cfg(dev, mode); kfree(buffer); /* wait at least 3 frames before continuing */ if (mode->restart) { - wait_event_timeout(dev->wait_setmode[chn], - (dev->setmode_ready[chn] != 0), + wait_event_timeout(channel->wait_setmode, + (channel->setmode_ready != 0), msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); - if (dev->setmode_ready[chn] != 1) { + if (channel->setmode_ready != 1) { printk(KERN_DEBUG "s2255: no set mode response\n"); res = -EFAULT; } } /* clear the restart flag */ - dev->mode[chn].restart = 0; + channel->mode.restart = 0; mutex_unlock(&dev->lock); - dprintk(1, "%s chn %lu, result: %d\n", __func__, chn, res); + dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res); return res; } -static int s2255_cmd_status(struct s2255_dev *dev, unsigned long chn, - u32 *pstatus) +static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus) { int res; __le32 *buffer; u32 chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); mutex_lock(&dev->lock); - chn_rev = G_chnmap[chn]; - dprintk(4, "%s chan %lu\n", __func__, chn); + chn_rev = G_chnmap[channel->idx]; + dprintk(4, "%s chan %d\n", __func__, channel->idx); buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -1270,17 +1286,17 @@ static int s2255_cmd_status(struct s2255_dev *dev, unsigned long chn, buffer[1] = (__le32) cpu_to_le32(chn_rev); buffer[2] = CMD_STATUS; *pstatus = 0; - dev->vidstatus_ready[chn] = 0; + channel->vidstatus_ready = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); kfree(buffer); - wait_event_timeout(dev->wait_vidstatus[chn], - (dev->vidstatus_ready[chn] != 0), + wait_event_timeout(channel->wait_vidstatus, + (channel->vidstatus_ready != 0), msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); - if (dev->vidstatus_ready[chn] != 1) { + if (channel->vidstatus_ready != 1) { printk(KERN_DEBUG "s2255: no vidstatus response\n"); res = -EFAULT; } - *pstatus = dev->vidstatus[chn]; + *pstatus = channel->vidstatus; dprintk(4, "%s, vid status %d\n", __func__, *pstatus); mutex_unlock(&dev->lock); return res; @@ -1291,9 +1307,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) int res; struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; - struct s2255_mode *new_mode; - struct s2255_mode *old_mode; - int chn; + struct s2255_channel *channel = fh->channel; int j; dprintk(4, "%s\n", __func__); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -1305,51 +1319,32 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (!res_get(dev, fh)) { + if (!res_get(fh)) { s2255_dev_err(&dev->udev->dev, "stream busy\n"); return -EBUSY; } - - /* send a set mode command everytime with restart. - in case we switch resolutions or other parameters */ - chn = fh->channel; - new_mode = &fh->mode; - old_mode = &fh->dev->mode[chn]; - - if ((new_mode->color & MASK_COLOR) != (old_mode->color & MASK_COLOR)) - new_mode->restart = 1; - else if (new_mode->scale != old_mode->scale) - new_mode->restart = 1; - else if (new_mode->format != old_mode->format) - new_mode->restart = 1; - - s2255_set_mode(dev, chn, new_mode); - new_mode->restart = 0; - *old_mode = *new_mode; - dev->cur_fmt[chn] = fh->fmt; - dev->last_frame[chn] = -1; - dev->bad_payload[chn] = 0; - dev->cur_frame[chn] = 0; - dev->frame_count[chn] = 0; + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; + channel->frame_count = 0; for (j = 0; j < SYS_FRAMES; j++) { - dev->buffer[chn].frame[j].ulState = S2255_READ_IDLE; - dev->buffer[chn].frame[j].cur_size = 0; + channel->buffer.frame[j].ulState = S2255_READ_IDLE; + channel->buffer.frame[j].cur_size = 0; } res = videobuf_streamon(&fh->vb_vidq); if (res == 0) { - s2255_start_acquire(dev, chn); - dev->b_acquire[chn] = 1; - } else { - res_free(dev, fh); - } + s2255_start_acquire(channel); + channel->b_acquire = 1; + } else + res_free(fh); + return res; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - dprintk(4, "%s\n, channel: %d", __func__, fh->channel); + dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_ERR "invalid fh type0\n"); return -EINVAL; @@ -1358,16 +1353,16 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) printk(KERN_ERR "invalid type i\n"); return -EINVAL; } - s2255_stop_acquire(dev, fh->channel); + s2255_stop_acquire(fh->channel); videobuf_streamoff(&fh->vb_vidq); - res_free(dev, fh); + res_free(fh); return 0; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) { struct s2255_fh *fh = priv; - struct s2255_mode *mode; + struct s2255_mode mode; struct videobuf_queue *q = &fh->vb_vidq; int ret = 0; mutex_lock(&q->vb_lock); @@ -1376,29 +1371,32 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) ret = -EBUSY; goto out_s_std; } - if (res_locked(fh->dev, fh)) { + if (res_locked(fh)) { dprintk(1, "can't change standard after started\n"); ret = -EBUSY; goto out_s_std; } - mode = &fh->mode; + mode = fh->channel->mode; if (*i & V4L2_STD_NTSC) { dprintk(4, "%s NTSC\n", __func__); /* if changing format, reset frame decimation/intervals */ - if (mode->format != FORMAT_NTSC) { - mode->format = FORMAT_NTSC; - mode->fdec = FDEC_1; + if (mode.format != FORMAT_NTSC) { + mode.restart = 1; + mode.format = FORMAT_NTSC; + mode.fdec = FDEC_1; } } else if (*i & V4L2_STD_PAL) { dprintk(4, "%s PAL\n", __func__); - mode->format = FORMAT_PAL; - if (mode->format != FORMAT_PAL) { - mode->format = FORMAT_PAL; - mode->fdec = FDEC_1; + if (mode.format != FORMAT_PAL) { + mode.restart = 1; + mode.format = FORMAT_PAL; + mode.fdec = FDEC_1; } } else { ret = -EINVAL; } + if (mode.restart) + s2255_set_mode(fh->channel, &mode); out_s_std: mutex_unlock(&q->vb_lock); return ret; @@ -1416,6 +1414,7 @@ static int vidioc_enum_input(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; u32 status = 0; if (inp->index != 0) return -EINVAL; @@ -1424,7 +1423,7 @@ static int vidioc_enum_input(struct file *file, void *priv, inp->status = 0; if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { int rc; - rc = s2255_cmd_status(dev, fh->channel, &status); + rc = s2255_cmd_status(fh->channel, &status); dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); if (rc == 0) inp->status = (status & 0x01) ? 0 @@ -1436,7 +1435,7 @@ static int vidioc_enum_input(struct file *file, void *priv, strlcpy(inp->name, "Composite", sizeof(inp->name)); break; case 0x2257: - strlcpy(inp->name, (fh->channel < 2) ? "Composite" : "S-Video", + strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video", sizeof(inp->name)); break; } @@ -1460,6 +1459,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; struct s2255_dev *dev = fh->dev; switch (qc->id) { case V4L2_CID_BRIGHTNESS: @@ -1477,7 +1477,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, case V4L2_CID_PRIVATE_COLORFILTER: if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) return -EINVAL; - if ((dev->pid == 0x2257) && (fh->channel > 1)) + if ((dev->pid == 0x2257) && (channel->idx > 1)) return -EINVAL; strlcpy(qc->name, "Color Filter", sizeof(qc->name)); qc->type = V4L2_CTRL_TYPE_MENU; @@ -1499,25 +1499,26 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = fh->mode.bright; + ctrl->value = channel->mode.bright; break; case V4L2_CID_CONTRAST: - ctrl->value = fh->mode.contrast; + ctrl->value = channel->mode.contrast; break; case V4L2_CID_SATURATION: - ctrl->value = fh->mode.saturation; + ctrl->value = channel->mode.saturation; break; case V4L2_CID_HUE: - ctrl->value = fh->mode.hue; + ctrl->value = channel->mode.hue; break; case V4L2_CID_PRIVATE_COLORFILTER: if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) return -EINVAL; - if ((dev->pid == 0x2257) && (fh->channel > 1)) + if ((dev->pid == 0x2257) && (channel->idx > 1)) return -EINVAL; - ctrl->value = !((fh->mode.color & MASK_INPUT_TYPE) >> 16); + ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); break; default: return -EINVAL; @@ -1530,41 +1531,42 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_mode *mode; - mode = &fh->mode; + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + struct s2255_mode mode; + mode = channel->mode; dprintk(4, "%s\n", __func__); /* update the mode to the corresponding value */ switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - mode->bright = ctrl->value; + mode.bright = ctrl->value; break; case V4L2_CID_CONTRAST: - mode->contrast = ctrl->value; + mode.contrast = ctrl->value; break; case V4L2_CID_HUE: - mode->hue = ctrl->value; + mode.hue = ctrl->value; break; case V4L2_CID_SATURATION: - mode->saturation = ctrl->value; + mode.saturation = ctrl->value; break; case V4L2_CID_PRIVATE_COLORFILTER: if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) return -EINVAL; - if ((dev->pid == 0x2257) && (fh->channel > 1)) + if ((dev->pid == 0x2257) && (channel->idx > 1)) return -EINVAL; - mode->color &= ~MASK_INPUT_TYPE; - mode->color |= ((ctrl->value ? 0 : 1) << 16); + mode.color &= ~MASK_INPUT_TYPE; + mode.color |= ((ctrl->value ? 0 : 1) << 16); break; default: return -EINVAL; } - mode->restart = 0; + mode.restart = 0; /* set mode here. Note: stream does not need restarted. some V4L programs restart stream unnecessarily after a s_crtl. */ - s2255_set_mode(dev, fh->channel, mode); + s2255_set_mode(fh->channel, &mode); return 0; } @@ -1572,8 +1574,8 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jc) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - *jc = dev->jc[fh->channel]; + struct s2255_channel *channel = fh->channel; + *jc = channel->jc; dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1582,10 +1584,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jc) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; if (jc->quality < 0 || jc->quality > 100) return -EINVAL; - dev->jc[fh->channel].quality = jc->quality; + channel->jc.quality = jc->quality; dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1594,17 +1596,17 @@ static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *sp) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; __u32 def_num, def_dem; + struct s2255_channel *channel = fh->channel; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memset(sp, 0, sizeof(struct v4l2_streamparm)); sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - sp->parm.capture.capturemode = dev->cap_parm[fh->channel].capturemode; - def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + sp->parm.capture.capturemode = channel->cap_parm.capturemode; + def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000; sp->parm.capture.timeperframe.denominator = def_dem; - switch (fh->mode.fdec) { + switch (channel->mode.fdec) { default: case FDEC_1: sp->parm.capture.timeperframe.numerator = def_num; @@ -1630,17 +1632,19 @@ static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *sp) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + struct s2255_mode mode; int fdec = FDEC_1; __u32 def_num, def_dem; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + mode = channel->mode; /* high quality capture mode requires a stream restart */ - if (dev->cap_parm[fh->channel].capturemode - != sp->parm.capture.capturemode && res_locked(fh->dev, fh)) + if (channel->cap_parm.capturemode + != sp->parm.capture.capturemode && res_locked(fh)) return -EBUSY; - def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; if (def_dem != sp->parm.capture.timeperframe.denominator) sp->parm.capture.timeperframe.numerator = def_num; else if (sp->parm.capture.timeperframe.numerator <= def_num) @@ -1655,9 +1659,9 @@ static int vidioc_s_parm(struct file *file, void *priv, sp->parm.capture.timeperframe.numerator = def_num * 5; fdec = FDEC_5; } - fh->mode.fdec = fdec; + mode.fdec = fdec; sp->parm.capture.timeperframe.denominator = def_dem; - s2255_set_mode(dev, fh->channel, &fh->mode); + s2255_set_mode(channel, &mode); dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", __func__, sp->parm.capture.capturemode, @@ -1707,24 +1711,13 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, static int s2255_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct s2255_dev *dev = video_drvdata(file); + struct s2255_channel *channel = video_drvdata(file); + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); struct s2255_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int i = 0; - int cur_channel = -1; int state; dprintk(1, "s2255: open called (dev=%s)\n", video_device_node_name(vdev)); - - for (i = 0; i < MAX_CHANNELS; i++) { - if (&dev->vdev[i] == vdev) { - cur_channel = i; - break; - } - } - if (i == MAX_CHANNELS) - return -ENODEV; - /* * open lock necessary to prevent multiple instances * of v4l-conf (or other programs) from simultaneously @@ -1806,24 +1799,20 @@ static int s2255_open(struct file *file) file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->mode = dev->mode[cur_channel]; - fh->fmt = dev->cur_fmt[cur_channel]; - /* default 4CIF NTSC */ - fh->width = LINE_SZ_4CIFS_NTSC; - fh->height = NUM_LINES_4CIFS_NTSC * 2; - fh->channel = cur_channel; - /* configure channel to default state */ - if (!dev->chn_configured[cur_channel]) { - s2255_set_mode(dev, cur_channel, &fh->mode); - dev->chn_configured[cur_channel] = 1; + fh->channel = channel; + if (!channel->configured) { + /* configure channel to default state */ + channel->fmt = &formats[0]; + s2255_set_mode(channel, &channel->mode); + channel->configured = 1; } dprintk(1, "%s: dev=%s type=%s\n", __func__, video_device_node_name(vdev), v4l2_type_names[type]); dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, (unsigned long)fh, (unsigned long)dev, - (unsigned long)&dev->vidq[cur_channel]); + (unsigned long)&channel->vidq); dprintk(4, "%s: list_empty active=%d\n", __func__, - list_empty(&dev->vidq[cur_channel].active)); + list_empty(&channel->vidq.active)); videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, NULL, &dev->slock, fh->type, @@ -1865,6 +1854,7 @@ static void s2255_destroy(struct s2255_dev *dev) mutex_destroy(&dev->open_lock); mutex_destroy(&dev->lock); usb_put_dev(dev->udev); + v4l2_device_unregister(&dev->v4l2_dev); dprintk(1, "%s", __func__); kfree(dev); } @@ -1874,14 +1864,15 @@ static int s2255_release(struct file *file) struct s2255_fh *fh = file->private_data; struct s2255_dev *dev = fh->dev; struct video_device *vdev = video_devdata(file); + struct s2255_channel *channel = fh->channel; if (!dev) return -ENODEV; /* turn off stream */ if (res_check(fh)) { - if (dev->b_acquire[fh->channel]) - s2255_stop_acquire(dev, fh->channel); + if (channel->b_acquire) + s2255_stop_acquire(fh->channel); videobuf_streamoff(&fh->vb_vidq); - res_free(dev, fh); + res_free(fh); } videobuf_mmap_free(&fh->vb_vidq); dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); @@ -1945,9 +1936,10 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { static void s2255_video_device_release(struct video_device *vdev) { - struct s2255_dev *dev = video_get_drvdata(vdev); - dprintk(4, "%s, chnls: %d \n", __func__, atomic_read(&dev->channels)); - if (atomic_dec_and_test(&dev->channels)) + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); + dprintk(4, "%s, chnls: %d \n", __func__, + atomic_read(&dev->num_channels)); + if (atomic_dec_and_test(&dev->num_channels)) s2255_destroy(dev); return; } @@ -1966,47 +1958,48 @@ static int s2255_probe_v4l(struct s2255_dev *dev) int ret; int i; int cur_nr = video_nr; + struct s2255_channel *channel; ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); if (ret) return ret; /* initialize all video 4 linux */ /* register 4 video devices */ for (i = 0; i < MAX_CHANNELS; i++) { - INIT_LIST_HEAD(&dev->vidq[i].active); - dev->vidq[i].dev = dev; - dev->vidq[i].channel = i; + channel = &dev->channel[i]; + INIT_LIST_HEAD(&channel->vidq.active); + channel->vidq.dev = dev; /* register 4 video devices */ - memcpy(&dev->vdev[i], &template, sizeof(struct video_device)); - dev->vdev[i].v4l2_dev = &dev->v4l2_dev; - video_set_drvdata(&dev->vdev[i], dev); + channel->vdev = template; + channel->vdev.v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(&channel->vdev, channel); if (video_nr == -1) - ret = video_register_device(&dev->vdev[i], + ret = video_register_device(&channel->vdev, VFL_TYPE_GRABBER, video_nr); else - ret = video_register_device(&dev->vdev[i], + ret = video_register_device(&channel->vdev, VFL_TYPE_GRABBER, cur_nr + i); + if (ret) { dev_err(&dev->udev->dev, "failed to register video device!\n"); break; } - atomic_inc(&dev->channels); + atomic_inc(&dev->num_channels); v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(&dev->vdev[i])); + video_device_node_name(&channel->vdev)); } - printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n", S2255_MAJOR_VERSION, S2255_MINOR_VERSION); /* if no channels registered, return error and probe will fail*/ - if (atomic_read(&dev->channels) == 0) { + if (atomic_read(&dev->num_channels) == 0) { v4l2_device_unregister(&dev->v4l2_dev); return ret; } - if (atomic_read(&dev->channels) != MAX_CHANNELS) + if (atomic_read(&dev->num_channels) != MAX_CHANNELS) printk(KERN_WARNING "s2255: Not all channels available.\n"); return 0; } @@ -2033,12 +2026,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) s32 idx = -1; struct s2255_framei *frm; unsigned char *pdata; - + struct s2255_channel *channel; dprintk(100, "buffer to user\n"); - - idx = dev->cur_frame[dev->cc]; - frm = &dev->buffer[dev->cc].frame[idx]; - + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; if (frm->ulState == S2255_READ_IDLE) { int jj; unsigned int cc; @@ -2063,16 +2055,18 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) } /* reverse it */ dev->cc = G_chnmap[cc]; + channel = &dev->channel[dev->cc]; payload = pdword[3]; - if (payload > dev->req_image_size[dev->cc]) { - dev->bad_payload[dev->cc]++; + if (payload > channel->req_image_size) { + channel->bad_payload++; /* discard the bad frame */ return -EINVAL; } - dev->pkt_size[dev->cc] = payload; - dev->jpg_size[dev->cc] = pdword[4]; + channel->pkt_size = payload; + channel->jpg_size = pdword[4]; break; case S2255_MARKER_RESPONSE: + pdata += DEF_USB_BLOCK; jj += DEF_USB_BLOCK; if (pdword[1] >= MAX_CHANNELS) @@ -2080,12 +2074,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) cc = G_chnmap[pdword[1]]; if (cc >= MAX_CHANNELS) break; + channel = &dev->channel[cc]; switch (pdword[2]) { case S2255_RESPONSE_SETMODE: /* check if channel valid */ /* set mode ready */ - dev->setmode_ready[cc] = 1; - wake_up(&dev->wait_setmode[cc]); + channel->setmode_ready = 1; + wake_up(&channel->wait_setmode); dprintk(5, "setmode ready %d\n", cc); break; case S2255_RESPONSE_FW: @@ -2099,9 +2094,9 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) wake_up(&dev->fw_data->wait_fw); break; case S2255_RESPONSE_STATUS: - dev->vidstatus[cc] = pdword[3]; - dev->vidstatus_ready[cc] = 1; - wake_up(&dev->wait_vidstatus[cc]); + channel->vidstatus = pdword[3]; + channel->vidstatus_ready = 1; + wake_up(&channel->wait_vidstatus); dprintk(5, "got vidstatus %x chan %d\n", pdword[3], cc); break; @@ -2118,13 +2113,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (!bframe) return -EINVAL; } - - - idx = dev->cur_frame[dev->cc]; - frm = &dev->buffer[dev->cc].frame[idx]; - + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; /* search done. now find out if should be acquiring on this channel */ - if (!dev->b_acquire[dev->cc]) { + if (!channel->b_acquire) { /* we found a frame, but this channel is turned off */ frm->ulState = S2255_READ_IDLE; return -EINVAL; @@ -2149,30 +2142,28 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) copy_size = (pipe_info->cur_transfer_size - offset); - size = dev->pkt_size[dev->cc] - PREFIX_SIZE; + size = channel->pkt_size - PREFIX_SIZE; /* sanity check on pdest */ - if ((copy_size + frm->cur_size) < dev->req_image_size[dev->cc]) + if ((copy_size + frm->cur_size) < channel->req_image_size) memcpy(pdest, psrc, copy_size); frm->cur_size += copy_size; dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); if (frm->cur_size >= size) { - - u32 cc = dev->cc; dprintk(2, "****************[%d]Buffer[%d]full*************\n", - cc, idx); - dev->last_frame[cc] = dev->cur_frame[cc]; - dev->cur_frame[cc]++; + dev->cc, idx); + channel->last_frame = channel->cur_frame; + channel->cur_frame++; /* end of system frame ring buffer, start at zero */ - if ((dev->cur_frame[cc] == SYS_FRAMES) || - (dev->cur_frame[cc] == dev->buffer[cc].dwFrames)) - dev->cur_frame[cc] = 0; + if ((channel->cur_frame == SYS_FRAMES) || + (channel->cur_frame == channel->buffer.dwFrames)) + channel->cur_frame = 0; /* frame ready */ - if (dev->b_acquire[cc]) - s2255_got_frame(dev, cc, dev->jpg_size[cc]); - dev->frame_count[cc]++; + if (channel->b_acquire) + s2255_got_frame(channel, channel->jpg_size); + channel->frame_count++; frm->ulState = S2255_READ_IDLE; frm->cur_size = 0; @@ -2245,16 +2236,12 @@ static int s2255_get_fx2fw(struct s2255_dev *dev) * Create the system ring buffer to copy frames into from the * usb read pipe. */ -static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn) +static int s2255_create_sys_buffers(struct s2255_channel *channel) { unsigned long i; unsigned long reqsize; dprintk(1, "create sys buffers\n"); - if (chn >= MAX_CHANNELS) - return -1; - - dev->buffer[chn].dwFrames = SYS_FRAMES; - + channel->buffer.dwFrames = SYS_FRAMES; /* always allocate maximum size(PAL) for system buffers */ reqsize = SYS_FRAMES_MAXSIZE; @@ -2263,42 +2250,40 @@ static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn) for (i = 0; i < SYS_FRAMES; i++) { /* allocate the frames */ - dev->buffer[chn].frame[i].lpvbits = vmalloc(reqsize); - - dprintk(1, "valloc %p chan %lu, idx %lu, pdata %p\n", - &dev->buffer[chn].frame[i], chn, i, - dev->buffer[chn].frame[i].lpvbits); - dev->buffer[chn].frame[i].size = reqsize; - if (dev->buffer[chn].frame[i].lpvbits == NULL) { + channel->buffer.frame[i].lpvbits = vmalloc(reqsize); + dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n", + &channel->buffer.frame[i], channel->idx, i, + channel->buffer.frame[i].lpvbits); + channel->buffer.frame[i].size = reqsize; + if (channel->buffer.frame[i].lpvbits == NULL) { printk(KERN_INFO "out of memory. using less frames\n"); - dev->buffer[chn].dwFrames = i; + channel->buffer.dwFrames = i; break; } } /* make sure internal states are set */ for (i = 0; i < SYS_FRAMES; i++) { - dev->buffer[chn].frame[i].ulState = 0; - dev->buffer[chn].frame[i].cur_size = 0; + channel->buffer.frame[i].ulState = 0; + channel->buffer.frame[i].cur_size = 0; } - dev->cur_frame[chn] = 0; - dev->last_frame[chn] = -1; + channel->cur_frame = 0; + channel->last_frame = -1; return 0; } -static int s2255_release_sys_buffers(struct s2255_dev *dev, - unsigned long channel) +static int s2255_release_sys_buffers(struct s2255_channel *channel) { unsigned long i; dprintk(1, "release sys buffers\n"); for (i = 0; i < SYS_FRAMES; i++) { - if (dev->buffer[channel].frame[i].lpvbits) { + if (channel->buffer.frame[i].lpvbits) { dprintk(1, "vfree %p\n", - dev->buffer[channel].frame[i].lpvbits); - vfree(dev->buffer[channel].frame[i].lpvbits); + channel->buffer.frame[i].lpvbits); + vfree(channel->buffer.frame[i].lpvbits); } - dev->buffer[channel].frame[i].lpvbits = NULL; + channel->buffer.frame[i].lpvbits = NULL; } return 0; } @@ -2335,17 +2320,20 @@ static int s2255_board_init(struct s2255_dev *dev) fw_ver & 0xff); for (j = 0; j < MAX_CHANNELS; j++) { - dev->b_acquire[j] = 0; - dev->mode[j] = mode_def; + struct s2255_channel *channel = &dev->channel[j]; + channel->b_acquire = 0; + channel->mode = mode_def; if (dev->pid == 0x2257 && j > 1) - dev->mode[j].color |= (1 << 16); - dev->jc[j].quality = S2255_DEF_JPEG_QUAL; - dev->cur_fmt[j] = &formats[0]; - dev->mode[j].restart = 1; - dev->req_image_size[j] = get_transfer_size(&mode_def); - dev->frame_count[j] = 0; + channel->mode.color |= (1 << 16); + channel->jc.quality = S2255_DEF_JPEG_QUAL; + channel->width = LINE_SZ_4CIFS_NTSC; + channel->height = NUM_LINES_4CIFS_NTSC * 2; + channel->fmt = &formats[0]; + channel->mode.restart = 1; + channel->req_image_size = get_transfer_size(&mode_def); + channel->frame_count = 0; /* create the system buffers */ - s2255_create_sys_buffers(dev, j); + s2255_create_sys_buffers(channel); } /* start read pipe */ s2255_start_readpipe(dev); @@ -2359,14 +2347,12 @@ static int s2255_board_shutdown(struct s2255_dev *dev) dprintk(1, "%s: dev: %p", __func__, dev); for (i = 0; i < MAX_CHANNELS; i++) { - if (dev->b_acquire[i]) - s2255_stop_acquire(dev, i); + if (dev->channel[i].b_acquire) + s2255_stop_acquire(&dev->channel[i]); } - s2255_stop_readpipe(dev); - for (i = 0; i < MAX_CHANNELS; i++) - s2255_release_sys_buffers(dev, i); + s2255_release_sys_buffers(&dev->channel[i]); /* release transfer buffer */ kfree(dev->pipe.transfer_buffer); return 0; @@ -2459,29 +2445,26 @@ static int s2255_start_readpipe(struct s2255_dev *dev) } /* starts acquisition process */ -static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) +static int s2255_start_acquire(struct s2255_channel *channel) { unsigned char *buffer; int res; unsigned long chn_rev; int j; - if (chn >= MAX_CHANNELS) { - dprintk(2, "start acquire failed, bad channel %lu\n", chn); - return -1; - } - chn_rev = G_chnmap[chn]; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); return -ENOMEM; } - dev->last_frame[chn] = -1; - dev->bad_payload[chn] = 0; - dev->cur_frame[chn] = 0; + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; for (j = 0; j < SYS_FRAMES; j++) { - dev->buffer[chn].frame[j].ulState = 0; - dev->buffer[chn].frame[j].cur_size = 0; + channel->buffer.frame[j].ulState = 0; + channel->buffer.frame[j].cur_size = 0; } /* send the start command */ @@ -2492,21 +2475,18 @@ static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) if (res != 0) dev_err(&dev->udev->dev, "CMD_START error\n"); - dprintk(2, "start acquire exit[%lu] %d \n", chn, res); + dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res); kfree(buffer); return 0; } -static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) +static int s2255_stop_acquire(struct s2255_channel *channel) { unsigned char *buffer; int res; unsigned long chn_rev; - if (chn >= MAX_CHANNELS) { - dprintk(2, "stop acquire failed, bad channel %lu\n", chn); - return -1; - } - chn_rev = G_chnmap[chn]; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -2520,8 +2500,8 @@ static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) if (res != 0) dev_err(&dev->udev->dev, "CMD_STOP error\n"); kfree(buffer); - dev->b_acquire[chn] = 0; - dprintk(4, "%s: chn %lu, res %d\n", __func__, chn, res); + channel->b_acquire = 0; + dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res); return res; } @@ -2575,7 +2555,7 @@ static int s2255_probe(struct usb_interface *interface, s2255_dev_err(&interface->dev, "out of memory\n"); return -ENOMEM; } - atomic_set(&dev->channels, 0); + atomic_set(&dev->num_channels, 0); dev->pid = id->idProduct; dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) @@ -2612,8 +2592,10 @@ static int s2255_probe(struct usb_interface *interface, dev->timer.data = (unsigned long)dev->fw_data; init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { - init_waitqueue_head(&dev->wait_setmode[i]); - init_waitqueue_head(&dev->wait_vidstatus[i]); + struct s2255_channel *channel = &dev->channel[i]; + dev->channel[i].idx = i; + init_waitqueue_head(&channel->wait_setmode); + init_waitqueue_head(&channel->wait_vidstatus); } dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -2651,7 +2633,7 @@ static int s2255_probe(struct usb_interface *interface, printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER) printk(KERN_WARNING "s2255: 2257 requires firmware %d" - "or above.\n", S2255_MIN_DSP_COLORFILTER); + " or above.\n", S2255_MIN_DSP_COLORFILTER); } usb_reset_device(dev->udev); /* load 2255 board specific */ @@ -2693,25 +2675,23 @@ static void s2255_disconnect(struct usb_interface *interface) { struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); int i; - int channels = atomic_read(&dev->channels); - v4l2_device_unregister(&dev->v4l2_dev); + int channels = atomic_read(&dev->num_channels); + v4l2_device_disconnect(&dev->v4l2_dev); /*see comments in the uvc_driver.c usb disconnect function */ - atomic_inc(&dev->channels); + atomic_inc(&dev->num_channels); /* unregister each video device. */ - for (i = 0; i < channels; i++) { - if (video_is_registered(&dev->vdev[i])) - video_unregister_device(&dev->vdev[i]); - } + for (i = 0; i < channels; i++) + video_unregister_device(&dev->channel[i].vdev); /* wake up any of our timers */ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); wake_up(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { - dev->setmode_ready[i] = 1; - wake_up(&dev->wait_setmode[i]); - dev->vidstatus_ready[i] = 1; - wake_up(&dev->wait_vidstatus[i]); + dev->channel[i].setmode_ready = 1; + wake_up(&dev->channel[i].wait_setmode); + dev->channel[i].vidstatus_ready = 1; + wake_up(&dev->channel[i].wait_vidstatus); } - if (atomic_dec_and_test(&dev->channels)) + if (atomic_dec_and_test(&dev->num_channels)) s2255_destroy(dev); dev_info(&interface->dev, "%s\n", __func__); } diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile new file mode 100644 index 00000000000..0d9d54132ec --- /dev/null +++ b/drivers/media/video/s5p-fimc/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o +s5p-fimc-y := fimc-core.o fimc-reg.o diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c new file mode 100644 index 00000000000..b151c7be8a5 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -0,0 +1,1586 @@ +/* + * S5P camera interface (video postprocessor) driver + * + * Copyright (c) 2010 Samsung Electronics + * + * Sylwester Nawrocki, <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf-dma-contig.h> + +#include "fimc-core.h" + +static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" }; + +static struct fimc_fmt fimc_formats[] = { + { + .name = "RGB565", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .color = S5P_FIMC_RGB565, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "BGR666", + .fourcc = V4L2_PIX_FMT_BGR666, + .depth = 32, + .color = S5P_FIMC_RGB666, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "XRGB-8-8-8-8, 24 bpp", + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 32, + .color = S5P_FIMC_RGB888, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .color = S5P_FIMC_YCBYCR422, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "YUV 4:2:2 packed, CbYCrY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .color = S5P_FIMC_CBYCRY422, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "YUV 4:2:2 packed, CrYCbY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .color = S5P_FIMC_CRYCBY422, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .color = S5P_FIMC_YCRYCB422, + .buff_cnt = 1, + .planes_cnt = 1 + }, { + .name = "YUV 4:2:2 planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 12, + .color = S5P_FIMC_YCBCR422, + .buff_cnt = 1, + .planes_cnt = 3 + }, { + .name = "YUV 4:2:2 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV16, + .depth = 16, + .color = S5P_FIMC_YCBCR422, + .buff_cnt = 1, + .planes_cnt = 2 + }, { + .name = "YUV 4:2:2 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV61, + .depth = 16, + .color = S5P_FIMC_RGB565, + .buff_cnt = 1, + .planes_cnt = 2 + }, { + .name = "YUV 4:2:0 planar, YCbCr", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .color = S5P_FIMC_YCBCR420, + .buff_cnt = 1, + .planes_cnt = 3 + }, { + .name = "YUV 4:2:0 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 12, + .color = S5P_FIMC_YCBCR420, + .buff_cnt = 1, + .planes_cnt = 2 + } + }; + +static struct v4l2_queryctrl fimc_ctrls[] = { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal flip", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_ROTATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Rotation (CCW)", + .minimum = 0, + .maximum = 270, + .step = 90, + .default_value = 0, + }, +}; + + +static struct v4l2_queryctrl *get_ctrl(int id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i) + if (id == fimc_ctrls[i].id) + return &fimc_ctrls[i]; + return NULL; +} + +static int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f) +{ + if (r->width > f->width) { + if (f->width > (r->width * SCALER_MAX_HRATIO)) + return -EINVAL; + } else { + if ((f->width * SCALER_MAX_HRATIO) < r->width) + return -EINVAL; + } + + if (r->height > f->height) { + if (f->height > (r->height * SCALER_MAX_VRATIO)) + return -EINVAL; + } else { + if ((f->height * SCALER_MAX_VRATIO) < r->height) + return -EINVAL; + } + + return 0; +} + +static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) +{ + if (src >= tar * 64) { + return -EINVAL; + } else if (src >= tar * 32) { + *ratio = 32; + *shift = 5; + } else if (src >= tar * 16) { + *ratio = 16; + *shift = 4; + } else if (src >= tar * 8) { + *ratio = 8; + *shift = 3; + } else if (src >= tar * 4) { + *ratio = 4; + *shift = 2; + } else if (src >= tar * 2) { + *ratio = 2; + *shift = 1; + } else { + *ratio = 1; + *shift = 0; + } + + return 0; +} + +static int fimc_set_scaler_info(struct fimc_ctx *ctx) +{ + struct fimc_scaler *sc = &ctx->scaler; + struct fimc_frame *s_frame = &ctx->s_frame; + struct fimc_frame *d_frame = &ctx->d_frame; + int tx, ty, sx, sy; + int ret; + + tx = d_frame->width; + ty = d_frame->height; + if (tx <= 0 || ty <= 0) { + v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, + "invalid target size: %d x %d", tx, ty); + return -EINVAL; + } + + sx = s_frame->width; + sy = s_frame->height; + if (sx <= 0 || sy <= 0) { + err("invalid source size: %d x %d", sx, sy); + return -EINVAL; + } + + sc->real_width = sx; + sc->real_height = sy; + dbg("sx= %d, sy= %d, tx= %d, ty= %d", sx, sy, tx, ty); + + ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor); + if (ret) + return ret; + + ret = fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor); + if (ret) + return ret; + + sc->pre_dst_width = sx / sc->pre_hratio; + sc->pre_dst_height = sy / sc->pre_vratio; + + sc->main_hratio = (sx << 8) / (tx << sc->hfactor); + sc->main_vratio = (sy << 8) / (ty << sc->vfactor); + + sc->scaleup_h = (tx >= sx) ? 1 : 0; + sc->scaleup_v = (ty >= sy) ? 1 : 0; + + /* check to see if input and output size/format differ */ + if (s_frame->fmt->color == d_frame->fmt->color + && s_frame->width == d_frame->width + && s_frame->height == d_frame->height) + sc->copy_mode = 1; + else + sc->copy_mode = 0; + + return 0; +} + + +static irqreturn_t fimc_isr(int irq, void *priv) +{ + struct fimc_vid_buffer *src_buf, *dst_buf; + struct fimc_dev *fimc = (struct fimc_dev *)priv; + struct fimc_ctx *ctx; + + BUG_ON(!fimc); + fimc_hw_clear_irq(fimc); + + spin_lock(&fimc->slock); + + if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { + ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); + if (!ctx || !ctx->m2m_ctx) + goto isr_unlock; + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (src_buf && dst_buf) { + spin_lock(&fimc->irqlock); + src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; + wake_up(&src_buf->vb.done); + wake_up(&dst_buf->vb.done); + spin_unlock(&fimc->irqlock); + v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); + } + } + +isr_unlock: + spin_unlock(&fimc->slock); + return IRQ_HANDLED; +} + +/* The color format (planes_cnt, buff_cnt) must be already configured. */ +static int fimc_prepare_addr(struct fimc_ctx *ctx, + struct fimc_vid_buffer *buf, enum v4l2_buf_type type) +{ + struct fimc_frame *frame; + struct fimc_addr *paddr; + u32 pix_size; + int ret = 0; + + frame = ctx_m2m_get_frame(ctx, type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + paddr = &frame->paddr; + + if (!buf) + return -EINVAL; + + pix_size = frame->width * frame->height; + + dbg("buff_cnt= %d, planes_cnt= %d, frame->size= %d, pix_size= %d", + frame->fmt->buff_cnt, frame->fmt->planes_cnt, + frame->size, pix_size); + + if (frame->fmt->buff_cnt == 1) { + paddr->y = videobuf_to_dma_contig(&buf->vb); + switch (frame->fmt->planes_cnt) { + case 1: + paddr->cb = 0; + paddr->cr = 0; + break; + case 2: + /* decompose Y into Y/Cb */ + paddr->cb = (u32)(paddr->y + pix_size); + paddr->cr = 0; + break; + case 3: + paddr->cb = (u32)(paddr->y + pix_size); + /* decompose Y into Y/Cb/Cr */ + if (S5P_FIMC_YCBCR420 == frame->fmt->color) + paddr->cr = (u32)(paddr->cb + + (pix_size >> 2)); + else /* 422 */ + paddr->cr = (u32)(paddr->cb + + (pix_size >> 1)); + break; + default: + return -EINVAL; + } + } + + dbg("PHYS_ADDR: type= %d, y= 0x%X cb= 0x%X cr= 0x%X ret= %d", + type, paddr->y, paddr->cb, paddr->cr, ret); + + return ret; +} + +/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */ +static void fimc_set_yuv_order(struct fimc_ctx *ctx) +{ + /* The one only mode supported in SoC. */ + ctx->in_order_2p = S5P_FIMC_LSB_CRCB; + ctx->out_order_2p = S5P_FIMC_LSB_CRCB; + + /* Set order for 1 plane input formats. */ + switch (ctx->s_frame.fmt->color) { + case S5P_FIMC_YCRYCB422: + ctx->in_order_1p = S5P_FIMC_IN_YCRYCB; + break; + case S5P_FIMC_CBYCRY422: + ctx->in_order_1p = S5P_FIMC_IN_CBYCRY; + break; + case S5P_FIMC_CRYCBY422: + ctx->in_order_1p = S5P_FIMC_IN_CRYCBY; + break; + case S5P_FIMC_YCBYCR422: + default: + ctx->in_order_1p = S5P_FIMC_IN_YCBYCR; + break; + } + dbg("ctx->in_order_1p= %d", ctx->in_order_1p); + + switch (ctx->d_frame.fmt->color) { + case S5P_FIMC_YCRYCB422: + ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB; + break; + case S5P_FIMC_CBYCRY422: + ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY; + break; + case S5P_FIMC_CRYCBY422: + ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY; + break; + case S5P_FIMC_YCBYCR422: + default: + ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR; + break; + } + dbg("ctx->out_order_1p= %d", ctx->out_order_1p); +} + +/** + * fimc_prepare_config - check dimensions, operation and color mode + * and pre-calculate offset and the scaling coefficients. + * + * @ctx: hardware context information + * @flags: flags indicating which parameters to check/update + * + * Return: 0 if dimensions are valid or non zero otherwise. + */ +static int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) +{ + struct fimc_frame *s_frame, *d_frame; + struct fimc_vid_buffer *buf = NULL; + struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + int ret = 0; + + s_frame = &ctx->s_frame; + d_frame = &ctx->d_frame; + + if (flags & FIMC_PARAMS) { + if ((ctx->out_path == FIMC_DMA) && + (ctx->rotation == 90 || ctx->rotation == 270)) { + swap(d_frame->f_width, d_frame->f_height); + swap(d_frame->width, d_frame->height); + } + + /* Prepare the output offset ratios for scaler. */ + d_frame->dma_offset.y_h = d_frame->offs_h; + if (!variant->pix_hoff) + d_frame->dma_offset.y_h *= (d_frame->fmt->depth >> 3); + + d_frame->dma_offset.y_v = d_frame->offs_v; + + d_frame->dma_offset.cb_h = d_frame->offs_h; + d_frame->dma_offset.cb_v = d_frame->offs_v; + + d_frame->dma_offset.cr_h = d_frame->offs_h; + d_frame->dma_offset.cr_v = d_frame->offs_v; + + if (!variant->pix_hoff && d_frame->fmt->planes_cnt == 3) { + d_frame->dma_offset.cb_h >>= 1; + d_frame->dma_offset.cb_v >>= 1; + d_frame->dma_offset.cr_h >>= 1; + d_frame->dma_offset.cr_v >>= 1; + } + + dbg("out offset: color= %d, y_h= %d, y_v= %d", + d_frame->fmt->color, + d_frame->dma_offset.y_h, d_frame->dma_offset.y_v); + + /* Prepare the input offset ratios for scaler. */ + s_frame->dma_offset.y_h = s_frame->offs_h; + if (!variant->pix_hoff) + s_frame->dma_offset.y_h *= (s_frame->fmt->depth >> 3); + s_frame->dma_offset.y_v = s_frame->offs_v; + + s_frame->dma_offset.cb_h = s_frame->offs_h; + s_frame->dma_offset.cb_v = s_frame->offs_v; + + s_frame->dma_offset.cr_h = s_frame->offs_h; + s_frame->dma_offset.cr_v = s_frame->offs_v; + + if (!variant->pix_hoff && s_frame->fmt->planes_cnt == 3) { + s_frame->dma_offset.cb_h >>= 1; + s_frame->dma_offset.cb_v >>= 1; + s_frame->dma_offset.cr_h >>= 1; + s_frame->dma_offset.cr_v >>= 1; + } + + dbg("in offset: color= %d, y_h= %d, y_v= %d", + s_frame->fmt->color, s_frame->dma_offset.y_h, + s_frame->dma_offset.y_v); + + fimc_set_yuv_order(ctx); + + /* Check against the scaler ratio. */ + if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) || + s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) { + err("out of scaler range"); + return -EINVAL; + } + } + + /* Input DMA mode is not allowed when the scaler is disabled. */ + ctx->scaler.enabled = 1; + + if (flags & FIMC_SRC_ADDR) { + buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, buf, + V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (ret) + return ret; + } + + if (flags & FIMC_DST_ADDR) { + buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, buf, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + } + + return ret; +} + +static void fimc_dma_run(void *priv) +{ + struct fimc_ctx *ctx = priv; + struct fimc_dev *fimc; + unsigned long flags; + u32 ret; + + if (WARN(!ctx, "null hardware context")) + return; + + fimc = ctx->fimc_dev; + + spin_lock_irqsave(&ctx->slock, flags); + set_bit(ST_M2M_PEND, &fimc->state); + + ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); + ret = fimc_prepare_config(ctx, ctx->state); + if (ret) { + err("general configuration error"); + goto dma_unlock; + } + + if (fimc->m2m.ctx != ctx) + ctx->state |= FIMC_PARAMS; + + fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); + + if (ctx->state & FIMC_PARAMS) { + fimc_hw_set_input_path(ctx); + fimc_hw_set_in_dma(ctx); + if (fimc_set_scaler_info(ctx)) { + err("scaler configuration error"); + goto dma_unlock; + } + fimc_hw_set_prescaler(ctx); + fimc_hw_set_scaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx); + } + + fimc_hw_set_output_path(ctx); + if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS)) + fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr); + + if (ctx->state & FIMC_PARAMS) + fimc_hw_set_out_dma(ctx); + + if (ctx->scaler.enabled) + fimc_hw_start_scaler(fimc); + fimc_hw_en_capture(ctx); + + ctx->state = 0; + fimc_hw_start_in_dma(fimc); + + fimc->m2m.ctx = ctx; + +dma_unlock: + spin_unlock_irqrestore(&ctx->slock, flags); +} + +static void fimc_job_abort(void *priv) +{ + /* Nothing done in job_abort. */ +} + +static void fimc_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct fimc_ctx *ctx = vq->priv_data; + struct fimc_frame *frame; + + frame = ctx_m2m_get_frame(ctx, vq->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + *size = (frame->width * frame->height * frame->fmt->depth) >> 3; + if (0 == *count) + *count = 1; + return 0; +} + +static int fimc_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct fimc_ctx *ctx = vq->priv_data; + struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev; + struct fimc_frame *frame; + int ret; + + frame = ctx_m2m_get_frame(ctx, vq->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + if (vb->baddr) { + if (vb->bsize < frame->size) { + v4l2_err(v4l2_dev, + "User-provided buffer too small (%d < %d)\n", + vb->bsize, frame->size); + WARN_ON(1); + return -EINVAL; + } + } else if (vb->state != VIDEOBUF_NEEDS_INIT + && vb->bsize < frame->size) { + return -EINVAL; + } + + vb->width = frame->width; + vb->height = frame->height; + vb->bytesperline = (frame->width * frame->fmt->depth) >> 3; + vb->size = frame->size; + vb->field = field; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) { + v4l2_err(v4l2_dev, "Iolock failed\n"); + fimc_buf_release(vq, vb); + return ret; + } + } + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void fimc_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct fimc_ctx *ctx = vq->priv_data; + v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); +} + +static struct videobuf_queue_ops fimc_qops = { + .buf_setup = fimc_buf_setup, + .buf_prepare = fimc_buf_prepare, + .buf_queue = fimc_buf_queue, + .buf_release = fimc_buf_release, +}; + +static int fimc_m2m_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct fimc_ctx *ctx = file->private_data; + struct fimc_dev *fimc = ctx->fimc_dev; + + strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); + strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(1, 0, 0); + cap->capabilities = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int fimc_m2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct fimc_fmt *fmt; + + if (f->index >= ARRAY_SIZE(fimc_formats)) + return -EINVAL; + + fmt = &fimc_formats[f->index]; + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + return 0; +} + +static int fimc_m2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct fimc_ctx *ctx = priv; + struct fimc_frame *frame; + + frame = ctx_m2m_get_frame(ctx, f->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + f->fmt.pix.width = frame->width; + f->fmt.pix.height = frame->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = frame->fmt->fourcc; + + return 0; +} + +static struct fimc_fmt *find_format(struct v4l2_format *f) +{ + struct fimc_fmt *fmt; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { + fmt = &fimc_formats[i]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + break; + } + if (i == ARRAY_SIZE(fimc_formats)) + return NULL; + + return fmt; +} + +static int fimc_m2m_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct fimc_fmt *fmt; + u32 max_width, max_height, mod_x, mod_y; + struct fimc_ctx *ctx = priv; + struct fimc_dev *fimc = ctx->fimc_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct samsung_fimc_variant *variant = fimc->variant; + + fmt = find_format(f); + if (!fmt) { + v4l2_err(&fimc->m2m.v4l2_dev, + "Fourcc format (0x%X) invalid.\n", pix->pixelformat); + return -EINVAL; + } + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != pix->field) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + max_width = variant->scaler_dis_w; + max_height = variant->scaler_dis_w; + mod_x = variant->min_inp_pixsize; + mod_y = variant->min_inp_pixsize; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + max_width = variant->out_rot_dis_w; + max_height = variant->out_rot_dis_w; + mod_x = variant->min_out_pixsize; + mod_y = variant->min_out_pixsize; + } else { + err("Wrong stream type (%d)", f->type); + return -EINVAL; + } + + dbg("max_w= %d, max_h= %d", max_width, max_height); + + if (pix->height > max_height) + pix->height = max_height; + if (pix->width > max_width) + pix->width = max_width; + + if (tiled_fmt(fmt)) { + mod_x = 64; /* 64x32 tile */ + mod_y = 32; + } + + dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y); + + pix->width = (pix->width == 0) ? mod_x : ALIGN(pix->width, mod_x); + pix->height = (pix->height == 0) ? mod_y : ALIGN(pix->height, mod_y); + + if (pix->bytesperline == 0 || + pix->bytesperline * 8 / fmt->depth > pix->width) + pix->bytesperline = (pix->width * fmt->depth) >> 3; + + if (pix->sizeimage == 0) + pix->sizeimage = pix->height * pix->bytesperline; + + dbg("pix->bytesperline= %d, fmt->depth= %d", + pix->bytesperline, fmt->depth); + + return 0; +} + + +static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct fimc_ctx *ctx = priv; + struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev; + struct videobuf_queue *src_vq, *dst_vq; + struct fimc_frame *frame; + struct v4l2_pix_format *pix; + unsigned long flags; + int ret = 0; + + BUG_ON(!ctx); + + ret = fimc_m2m_try_fmt(file, priv, f); + if (ret) + return ret; + + mutex_lock(&ctx->fimc_dev->lock); + + src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx); + dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx); + + mutex_lock(&src_vq->vb_lock); + mutex_lock(&dst_vq->vb_lock); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (videobuf_queue_is_busy(src_vq)) { + v4l2_err(v4l2_dev, "%s queue busy\n", __func__); + ret = -EBUSY; + goto s_fmt_out; + } + frame = &ctx->s_frame; + spin_lock_irqsave(&ctx->slock, flags); + ctx->state |= FIMC_SRC_FMT; + spin_unlock_irqrestore(&ctx->slock, flags); + + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (videobuf_queue_is_busy(dst_vq)) { + v4l2_err(v4l2_dev, "%s queue busy\n", __func__); + ret = -EBUSY; + goto s_fmt_out; + } + frame = &ctx->d_frame; + spin_lock_irqsave(&ctx->slock, flags); + ctx->state |= FIMC_DST_FMT; + spin_unlock_irqrestore(&ctx->slock, flags); + } else { + v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, + "Wrong buffer/video queue type (%d)\n", f->type); + return -EINVAL; + } + + pix = &f->fmt.pix; + frame->fmt = find_format(f); + if (!frame->fmt) { + ret = -EINVAL; + goto s_fmt_out; + } + + frame->f_width = pix->bytesperline * 8 / frame->fmt->depth; + frame->f_height = pix->sizeimage/pix->bytesperline; + frame->width = pix->width; + frame->height = pix->height; + frame->o_width = pix->width; + frame->o_height = pix->height; + frame->offs_h = 0; + frame->offs_v = 0; + frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3; + src_vq->field = dst_vq->field = pix->field; + spin_lock_irqsave(&ctx->slock, flags); + ctx->state |= FIMC_PARAMS; + spin_unlock_irqrestore(&ctx->slock, flags); + + dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height); + +s_fmt_out: + mutex_unlock(&dst_vq->vb_lock); + mutex_unlock(&src_vq->vb_lock); + mutex_unlock(&ctx->fimc_dev->lock); + return ret; +} + +static int fimc_m2m_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct fimc_ctx *ctx = priv; + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int fimc_m2m_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = priv; + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = priv; + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = priv; + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = priv; + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int fimc_m2m_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = priv; + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +int fimc_m2m_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct v4l2_queryctrl *c; + c = get_ctrl(qc->id); + if (!c) + return -EINVAL; + *qc = *c; + return 0; +} + +int fimc_m2m_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct fimc_ctx *ctx = priv; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0; + break; + case V4L2_CID_VFLIP: + ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0; + break; + case V4L2_CID_ROTATE: + ctrl->value = ctx->rotation; + break; + default: + v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + dbg("ctrl->value= %d", ctrl->value); + return 0; +} + +static int check_ctrl_val(struct fimc_ctx *ctx, + struct v4l2_control *ctrl) +{ + struct v4l2_queryctrl *c; + c = get_ctrl(ctrl->id); + if (!c) + return -EINVAL; + + if (ctrl->value < c->minimum || ctrl->value > c->maximum + || (c->step != 0 && ctrl->value % c->step != 0)) { + v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, + "Invalid control value\n"); + return -ERANGE; + } + + return 0; +} + +int fimc_m2m_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct fimc_ctx *ctx = priv; + struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + unsigned long flags; + int ret = 0; + + ret = check_ctrl_val(ctx, ctrl); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + if (ctx->rotation != 0) + return 0; + if (ctrl->value) + ctx->flip |= FLIP_X_AXIS; + else + ctx->flip &= ~FLIP_X_AXIS; + break; + + case V4L2_CID_VFLIP: + if (ctx->rotation != 0) + return 0; + if (ctrl->value) + ctx->flip |= FLIP_Y_AXIS; + else + ctx->flip &= ~FLIP_Y_AXIS; + break; + + case V4L2_CID_ROTATE: + if (ctrl->value == 90 || ctrl->value == 270) { + if (ctx->out_path == FIMC_LCDFIFO && + !variant->has_inp_rot) { + return -EINVAL; + } else if (ctx->in_path == FIMC_DMA && + !variant->has_out_rot) { + return -EINVAL; + } + } + ctx->rotation = ctrl->value; + if (ctrl->value == 180) + ctx->flip = FLIP_XY_AXIS; + break; + + default: + v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + spin_lock_irqsave(&ctx->slock, flags); + ctx->state |= FIMC_PARAMS; + spin_unlock_irqrestore(&ctx->slock, flags); + return 0; +} + + +static int fimc_m2m_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cr) +{ + struct fimc_frame *frame; + struct fimc_ctx *ctx = fh; + + frame = ctx_m2m_get_frame(ctx, cr->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + cr->bounds.left = 0; + cr->bounds.top = 0; + cr->bounds.width = frame->f_width; + cr->bounds.height = frame->f_height; + cr->defrect.left = frame->offs_h; + cr->defrect.top = frame->offs_v; + cr->defrect.width = frame->o_width; + cr->defrect.height = frame->o_height; + return 0; +} + +static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) +{ + struct fimc_frame *frame; + struct fimc_ctx *ctx = file->private_data; + + frame = ctx_m2m_get_frame(ctx, cr->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + cr->c.left = frame->offs_h; + cr->c.top = frame->offs_v; + cr->c.width = frame->width; + cr->c.height = frame->height; + + return 0; +} + +static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +{ + struct fimc_ctx *ctx = file->private_data; + struct fimc_dev *fimc = ctx->fimc_dev; + unsigned long flags; + struct fimc_frame *f; + u32 min_size; + int ret = 0; + + if (cr->c.top < 0 || cr->c.left < 0) { + v4l2_err(&fimc->m2m.v4l2_dev, + "doesn't support negative values for top & left\n"); + return -EINVAL; + } + + if (cr->c.width <= 0 || cr->c.height <= 0) { + v4l2_err(&fimc->m2m.v4l2_dev, + "crop width and height must be greater than 0\n"); + return -EINVAL; + } + + f = ctx_m2m_get_frame(ctx, cr->type); + if (IS_ERR(f)) + return PTR_ERR(f); + + /* Adjust to required pixel boundary. */ + min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? + fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; + + cr->c.width = round_down(cr->c.width, min_size); + cr->c.height = round_down(cr->c.height, min_size); + cr->c.left = round_down(cr->c.left + 1, min_size); + cr->c.top = round_down(cr->c.top + 1, min_size); + + if ((cr->c.left + cr->c.width > f->o_width) + || (cr->c.top + cr->c.height > f->o_height)) { + v4l2_err(&fimc->m2m.v4l2_dev, "Error in S_CROP params\n"); + return -EINVAL; + } + + spin_lock_irqsave(&ctx->slock, flags); + if ((ctx->state & FIMC_SRC_FMT) && (ctx->state & FIMC_DST_FMT)) { + /* Check for the pixel scaling ratio when cropping input img. */ + if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); + else if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame); + + if (ret) { + spin_unlock_irqrestore(&ctx->slock, flags); + v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range"); + return -EINVAL; + } + } + ctx->state |= FIMC_PARAMS; + spin_unlock_irqrestore(&ctx->slock, flags); + + f->offs_h = cr->c.left; + f->offs_v = cr->c.top; + f->width = cr->c.width; + f->height = cr->c.height; + return 0; +} + +static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { + .vidioc_querycap = fimc_m2m_querycap, + + .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt, + .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt, + + .vidioc_g_fmt_vid_cap = fimc_m2m_g_fmt, + .vidioc_g_fmt_vid_out = fimc_m2m_g_fmt, + + .vidioc_try_fmt_vid_cap = fimc_m2m_try_fmt, + .vidioc_try_fmt_vid_out = fimc_m2m_try_fmt, + + .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt, + .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt, + + .vidioc_reqbufs = fimc_m2m_reqbufs, + .vidioc_querybuf = fimc_m2m_querybuf, + + .vidioc_qbuf = fimc_m2m_qbuf, + .vidioc_dqbuf = fimc_m2m_dqbuf, + + .vidioc_streamon = fimc_m2m_streamon, + .vidioc_streamoff = fimc_m2m_streamoff, + + .vidioc_queryctrl = fimc_m2m_queryctrl, + .vidioc_g_ctrl = fimc_m2m_g_ctrl, + .vidioc_s_ctrl = fimc_m2m_s_ctrl, + + .vidioc_g_crop = fimc_m2m_g_crop, + .vidioc_s_crop = fimc_m2m_s_crop, + .vidioc_cropcap = fimc_m2m_cropcap + +}; + +static void queue_init(void *priv, struct videobuf_queue *vq, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = priv; + struct fimc_dev *fimc = ctx->fimc_dev; + + videobuf_queue_dma_contig_init(vq, &fimc_qops, + fimc->m2m.v4l2_dev.dev, + &fimc->irqlock, type, V4L2_FIELD_NONE, + sizeof(struct fimc_vid_buffer), priv); +} + +static int fimc_m2m_open(struct file *file) +{ + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_ctx *ctx = NULL; + int err = 0; + + mutex_lock(&fimc->lock); + fimc->m2m.refcnt++; + set_bit(ST_OUTDMA_RUN, &fimc->state); + mutex_unlock(&fimc->lock); + + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + file->private_data = ctx; + ctx->fimc_dev = fimc; + /* default format */ + ctx->s_frame.fmt = &fimc_formats[0]; + ctx->d_frame.fmt = &fimc_formats[0]; + /* per user process device context initialization */ + ctx->state = 0; + ctx->flags = 0; + ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL; + ctx->in_path = FIMC_DMA; + ctx->out_path = FIMC_DMA; + spin_lock_init(&ctx->slock); + + ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, fimc->m2m.m2m_dev, queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + err = PTR_ERR(ctx->m2m_ctx); + kfree(ctx); + } + return err; +} + +static int fimc_m2m_release(struct file *file) +{ + struct fimc_ctx *ctx = file->private_data; + struct fimc_dev *fimc = ctx->fimc_dev; + + v4l2_m2m_ctx_release(ctx->m2m_ctx); + kfree(ctx); + mutex_lock(&fimc->lock); + if (--fimc->m2m.refcnt <= 0) + clear_bit(ST_OUTDMA_RUN, &fimc->state); + mutex_unlock(&fimc->lock); + return 0; +} + +static unsigned int fimc_m2m_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct fimc_ctx *ctx = file->private_data; + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +} + + +static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fimc_ctx *ctx = file->private_data; + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations fimc_m2m_fops = { + .owner = THIS_MODULE, + .open = fimc_m2m_open, + .release = fimc_m2m_release, + .poll = fimc_m2m_poll, + .ioctl = video_ioctl2, + .mmap = fimc_m2m_mmap, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = fimc_dma_run, + .job_abort = fimc_job_abort, +}; + + +static int fimc_register_m2m_device(struct fimc_dev *fimc) +{ + struct video_device *vfd; + struct platform_device *pdev; + struct v4l2_device *v4l2_dev; + int ret = 0; + + if (!fimc) + return -ENODEV; + + pdev = fimc->pdev; + v4l2_dev = &fimc->m2m.v4l2_dev; + + /* set name if it is empty */ + if (!v4l2_dev->name[0]) + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), + "%s.m2m", dev_name(&pdev->dev)); + + ret = v4l2_device_register(&pdev->dev, v4l2_dev); + if (ret) + return ret;; + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(v4l2_dev, "Failed to allocate video device\n"); + goto err_m2m_r1; + } + + vfd->fops = &fimc_m2m_fops; + vfd->ioctl_ops = &fimc_m2m_ioctl_ops; + vfd->minor = -1; + vfd->release = video_device_release; + + snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev)); + + video_set_drvdata(vfd, fimc); + platform_set_drvdata(pdev, fimc); + + fimc->m2m.vfd = vfd; + fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(fimc->m2m.m2m_dev)) { + v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); + ret = PTR_ERR(fimc->m2m.m2m_dev); + goto err_m2m_r2; + } + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(v4l2_dev, + "%s(): failed to register video device\n", __func__); + goto err_m2m_r3; + } + v4l2_info(v4l2_dev, + "FIMC m2m driver registered as /dev/video%d\n", vfd->num); + + return 0; + +err_m2m_r3: + v4l2_m2m_release(fimc->m2m.m2m_dev); +err_m2m_r2: + video_device_release(fimc->m2m.vfd); +err_m2m_r1: + v4l2_device_unregister(v4l2_dev); + + return ret; +} + +static void fimc_unregister_m2m_device(struct fimc_dev *fimc) +{ + if (fimc) { + v4l2_m2m_release(fimc->m2m.m2m_dev); + video_unregister_device(fimc->m2m.vfd); + video_device_release(fimc->m2m.vfd); + v4l2_device_unregister(&fimc->m2m.v4l2_dev); + } +} + +static void fimc_clk_release(struct fimc_dev *fimc) +{ + int i; + for (i = 0; i < NUM_FIMC_CLOCKS; i++) { + if (fimc->clock[i]) { + clk_disable(fimc->clock[i]); + clk_put(fimc->clock[i]); + } + } +} + +static int fimc_clk_get(struct fimc_dev *fimc) +{ + int i; + for (i = 0; i < NUM_FIMC_CLOCKS; i++) { + fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clock_name[i]); + if (IS_ERR(fimc->clock[i])) { + dev_err(&fimc->pdev->dev, + "failed to get fimc clock: %s\n", + fimc_clock_name[i]); + return -ENXIO; + } + clk_enable(fimc->clock[i]); + } + return 0; +} + +static int fimc_probe(struct platform_device *pdev) +{ + struct fimc_dev *fimc; + struct resource *res; + struct samsung_fimc_driverdata *drv_data; + int ret = 0; + + dev_dbg(&pdev->dev, "%s():\n", __func__); + + drv_data = (struct samsung_fimc_driverdata *) + platform_get_device_id(pdev)->driver_data; + + if (pdev->id >= drv_data->devs_cnt) { + dev_err(&pdev->dev, "Invalid platform device id: %d\n", + pdev->id); + return -EINVAL; + } + + fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL); + if (!fimc) + return -ENOMEM; + + fimc->id = pdev->id; + fimc->variant = drv_data->variant[fimc->id]; + fimc->pdev = pdev; + fimc->state = ST_IDLE; + + spin_lock_init(&fimc->irqlock); + spin_lock_init(&fimc->slock); + + mutex_init(&fimc->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to find the registers\n"); + ret = -ENOENT; + goto err_info; + } + + fimc->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!fimc->regs_res) { + dev_err(&pdev->dev, "failed to obtain register region\n"); + ret = -ENOENT; + goto err_info; + } + + fimc->regs = ioremap(res->start, resource_size(res)); + if (!fimc->regs) { + dev_err(&pdev->dev, "failed to map registers\n"); + ret = -ENXIO; + goto err_req_region; + } + + ret = fimc_clk_get(fimc); + if (ret) + goto err_regs_unmap; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get IRQ resource\n"); + ret = -ENXIO; + goto err_clk; + } + fimc->irq = res->start; + + fimc_hw_reset(fimc); + + ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc); + if (ret) { + dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); + goto err_clk; + } + + fimc->work_queue = create_workqueue(dev_name(&fimc->pdev->dev)); + if (!fimc->work_queue) + goto err_irq; + + ret = fimc_register_m2m_device(fimc); + if (ret) + goto err_wq; + + fimc_hw_en_lastirq(fimc, true); + + dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n", + __func__, fimc->id); + + return 0; + +err_wq: + destroy_workqueue(fimc->work_queue); +err_irq: + free_irq(fimc->irq, fimc); +err_clk: + fimc_clk_release(fimc); +err_regs_unmap: + iounmap(fimc->regs); +err_req_region: + release_resource(fimc->regs_res); + kfree(fimc->regs_res); +err_info: + kfree(fimc); + dev_err(&pdev->dev, "failed to install\n"); + return ret; +} + +static int __devexit fimc_remove(struct platform_device *pdev) +{ + struct fimc_dev *fimc = + (struct fimc_dev *)platform_get_drvdata(pdev); + + v4l2_info(&fimc->m2m.v4l2_dev, "Removing %s\n", pdev->name); + + free_irq(fimc->irq, fimc); + + fimc_hw_reset(fimc); + + fimc_unregister_m2m_device(fimc); + fimc_clk_release(fimc); + iounmap(fimc->regs); + release_resource(fimc->regs_res); + kfree(fimc->regs_res); + kfree(fimc); + return 0; +} + +static struct samsung_fimc_variant fimc01_variant_s5p = { + .has_inp_rot = 1, + .has_out_rot = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + + .scaler_en_w = 3264, + .scaler_dis_w = 8192, + .in_rot_en_h = 1920, + .in_rot_dis_w = 8192, + .out_rot_en_w = 1920, + .out_rot_dis_w = 4224, +}; + +static struct samsung_fimc_variant fimc2_variant_s5p = { + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + + .scaler_en_w = 4224, + .scaler_dis_w = 8192, + .in_rot_en_h = 1920, + .in_rot_dis_w = 8192, + .out_rot_en_w = 1920, + .out_rot_dis_w = 4224, +}; + +static struct samsung_fimc_variant fimc01_variant_s5pv210 = { + .has_inp_rot = 1, + .has_out_rot = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 32, + + .scaler_en_w = 4224, + .scaler_dis_w = 8192, + .in_rot_en_h = 1920, + .in_rot_dis_w = 8192, + .out_rot_en_w = 1920, + .out_rot_dis_w = 4224, +}; + +static struct samsung_fimc_variant fimc2_variant_s5pv210 = { + .min_inp_pixsize = 16, + .min_out_pixsize = 32, + + .scaler_en_w = 1920, + .scaler_dis_w = 8192, + .in_rot_en_h = 1280, + .in_rot_dis_w = 8192, + .out_rot_en_w = 1280, + .out_rot_dis_w = 1920, +}; + +static struct samsung_fimc_driverdata fimc_drvdata_s5p = { + .variant = { + [0] = &fimc01_variant_s5p, + [1] = &fimc01_variant_s5p, + [2] = &fimc2_variant_s5p, + }, + .devs_cnt = 3 +}; + +static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { + .variant = { + [0] = &fimc01_variant_s5pv210, + [1] = &fimc01_variant_s5pv210, + [2] = &fimc2_variant_s5pv210, + }, + .devs_cnt = 3 +}; + +static struct platform_device_id fimc_driver_ids[] = { + { + .name = "s5p-fimc", + .driver_data = (unsigned long)&fimc_drvdata_s5p, + }, { + .name = "s5pv210-fimc", + .driver_data = (unsigned long)&fimc_drvdata_s5pv210, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fimc_driver_ids); + +static struct platform_driver fimc_driver = { + .probe = fimc_probe, + .remove = __devexit_p(fimc_remove), + .id_table = fimc_driver_ids, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + } +}; + +static char banner[] __initdata = KERN_INFO + "S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n"; + +static int __init fimc_init(void) +{ + u32 ret; + printk(banner); + + ret = platform_driver_register(&fimc_driver); + if (ret) { + printk(KERN_ERR "FIMC platform driver register failed\n"); + return -1; + } + return 0; +} + +static void __exit fimc_exit(void) +{ + platform_driver_unregister(&fimc_driver); +} + +module_init(fimc_init); +module_exit(fimc_exit); + +MODULE_AUTHOR("Sylwester Nawrocki, s.nawrocki@samsung.com"); +MODULE_DESCRIPTION("S3C/S5P FIMC (video postprocessor) driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h new file mode 100644 index 00000000000..6b3e0cd73cd --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2010 Samsung Electronics + * + * Sylwester Nawrocki, <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_CORE_H_ +#define FIMC_CORE_H_ + +#include <linux/types.h> +#include <media/videobuf-core.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mem2mem.h> +#include <linux/videodev2.h> +#include "regs-fimc.h" + +#define err(fmt, args...) \ + printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args) + +#ifdef DEBUG +#define dbg(fmt, args...) \ + printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args) +#else +#define dbg(fmt, args...) +#endif + +#define NUM_FIMC_CLOCKS 2 +#define MODULE_NAME "s5p-fimc" +#define FIMC_MAX_DEVS 3 +#define FIMC_MAX_OUT_BUFS 4 +#define SCALER_MAX_HRATIO 64 +#define SCALER_MAX_VRATIO 64 + +enum { + ST_IDLE, + ST_OUTDMA_RUN, + ST_M2M_PEND, +}; + +#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state) +#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) + +enum fimc_datapath { + FIMC_ITU_CAM_A, + FIMC_ITU_CAM_B, + FIMC_MIPI_CAM, + FIMC_DMA, + FIMC_LCDFIFO, + FIMC_WRITEBACK +}; + +enum fimc_color_fmt { + S5P_FIMC_RGB565, + S5P_FIMC_RGB666, + S5P_FIMC_RGB888, + S5P_FIMC_YCBCR420, + S5P_FIMC_YCBCR422, + S5P_FIMC_YCBYCR422, + S5P_FIMC_YCRYCB422, + S5P_FIMC_CBYCRY422, + S5P_FIMC_CRYCBY422, + S5P_FIMC_RGB30_LOCAL, + S5P_FIMC_YCBCR444_LOCAL, + S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL, + S5P_FIMC_COLOR_MASK = 0x0F, +}; + +/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */ +#define S5P_FIMC_OUT_CRYCBY S5P_CIOCTRL_ORDER422_CRYCBY +#define S5P_FIMC_OUT_CBYCRY S5P_CIOCTRL_ORDER422_YCRYCB +#define S5P_FIMC_OUT_YCRYCB S5P_CIOCTRL_ORDER422_CBYCRY +#define S5P_FIMC_OUT_YCBYCR S5P_CIOCTRL_ORDER422_YCBYCR + +/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */ +#define S5P_FIMC_IN_CRYCBY S5P_MSCTRL_ORDER422_CRYCBY +#define S5P_FIMC_IN_CBYCRY S5P_MSCTRL_ORDER422_YCRYCB +#define S5P_FIMC_IN_YCRYCB S5P_MSCTRL_ORDER422_CBYCRY +#define S5P_FIMC_IN_YCBYCR S5P_MSCTRL_ORDER422_YCBYCR + +/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */ +#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB + +/* The embedded image effect selection */ +#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS +#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY +#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE +#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE +#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING +#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE + +/* The hardware context state. */ +#define FIMC_PARAMS (1 << 0) +#define FIMC_SRC_ADDR (1 << 1) +#define FIMC_DST_ADDR (1 << 2) +#define FIMC_SRC_FMT (1 << 3) +#define FIMC_DST_FMT (1 << 4) + +/* Image conversion flags */ +#define FIMC_IN_DMA_ACCESS_TILED (1 << 0) +#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0) +#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1) +#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1) +#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2) +#define FIMC_SCAN_MODE_INTERLACED (1 << 2) +/* YCbCr data dynamic range for RGB-YUV color conversion. Y/Cb/Cr: (0 ~ 255) */ +#define FIMC_COLOR_RANGE_WIDE (0 << 3) +/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */ +#define FIMC_COLOR_RANGE_NARROW (1 << 3) + +#define FLIP_NONE 0 +#define FLIP_X_AXIS 1 +#define FLIP_Y_AXIS 2 +#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS) + +/** + * struct fimc_fmt - the driver's internal color format data + * @name: format description + * @fourcc: the fourcc code for this format + * @color: the corresponding fimc_color_fmt + * @depth: number of bits per pixel + * @buff_cnt: number of physically non-contiguous data planes + * @planes_cnt: number of physically contiguous data planes + */ +struct fimc_fmt { + char *name; + u32 fourcc; + u32 color; + u32 depth; + u16 buff_cnt; + u16 planes_cnt; +}; + +/** + * struct fimc_dma_offset - pixel offset information for DMA + * @y_h: y value horizontal offset + * @y_v: y value vertical offset + * @cb_h: cb value horizontal offset + * @cb_v: cb value vertical offset + * @cr_h: cr value horizontal offset + * @cr_v: cr value vertical offset + */ +struct fimc_dma_offset { + int y_h; + int y_v; + int cb_h; + int cb_v; + int cr_h; + int cr_v; +}; + +/** + * struct fimc_effect - the configuration data for the "Arbitrary" image effect + * @type: effect type + * @pat_cb: cr value when type is "arbitrary" + * @pat_cr: cr value when type is "arbitrary" + */ +struct fimc_effect { + u32 type; + u8 pat_cb; + u8 pat_cr; +}; + +/** + * struct fimc_scaler - the configuration data for FIMC inetrnal scaler + * + * @enabled: the flag set when the scaler is used + * @hfactor: horizontal shift factor + * @vfactor: vertical shift factor + * @pre_hratio: horizontal ratio of the prescaler + * @pre_vratio: vertical ratio of the prescaler + * @pre_dst_width: the prescaler's destination width + * @pre_dst_height: the prescaler's destination height + * @scaleup_h: flag indicating scaling up horizontally + * @scaleup_v: flag indicating scaling up vertically + * @main_hratio: the main scaler's horizontal ratio + * @main_vratio: the main scaler's vertical ratio + * @real_width: source width - offset + * @real_height: source height - offset + * @copy_mode: flag set if one-to-one mode is used, i.e. no scaling + * and color format conversion + */ +struct fimc_scaler { + u32 enabled; + u32 hfactor; + u32 vfactor; + u32 pre_hratio; + u32 pre_vratio; + u32 pre_dst_width; + u32 pre_dst_height; + u32 scaleup_h; + u32 scaleup_v; + u32 main_hratio; + u32 main_vratio; + u32 real_width; + u32 real_height; + u32 copy_mode; +}; + +/** + * struct fimc_addr - the FIMC physical address set for DMA + * + * @y: luminance plane physical address + * @cb: Cb plane physical address + * @cr: Cr plane physical address + */ +struct fimc_addr { + u32 y; + u32 cb; + u32 cr; +}; + +/** + * struct fimc_vid_buffer - the driver's video buffer + * @vb: v4l videobuf buffer + */ +struct fimc_vid_buffer { + struct videobuf_buffer vb; +}; + +/** + * struct fimc_frame - input/output frame format properties + * + * @f_width: image full width (virtual screen size) + * @f_height: image full height (virtual screen size) + * @o_width: original image width as set by S_FMT + * @o_height: original image height as set by S_FMT + * @offs_h: image horizontal pixel offset + * @offs_v: image vertical pixel offset + * @width: image pixel width + * @height: image pixel weight + * @paddr: image frame buffer physical addresses + * @buf_cnt: number of buffers depending on a color format + * @size: image size in bytes + * @color: color format + * @dma_offset: DMA offset in bytes + */ +struct fimc_frame { + u32 f_width; + u32 f_height; + u32 o_width; + u32 o_height; + u32 offs_h; + u32 offs_v; + u32 width; + u32 height; + u32 size; + struct fimc_addr paddr; + struct fimc_dma_offset dma_offset; + struct fimc_fmt *fmt; +}; + +/** + * struct fimc_m2m_device - v4l2 memory-to-memory device data + * @vfd: the video device node for v4l2 m2m mode + * @v4l2_dev: v4l2 device for m2m mode + * @m2m_dev: v4l2 memory-to-memory device data + * @ctx: hardware context data + * @refcnt: the reference counter + */ +struct fimc_m2m_device { + struct video_device *vfd; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct fimc_ctx *ctx; + int refcnt; +}; + +/** + * struct samsung_fimc_variant - camera interface variant information + * + * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes + * @has_inp_rot: set if has input rotator + * @has_out_rot: set if has output rotator + * @min_inp_pixsize: minimum input pixel size + * @min_out_pixsize: minimum output pixel size + * @scaler_en_w: maximum input pixel width when the scaler is enabled + * @scaler_dis_w: maximum input pixel width when the scaler is disabled + * @in_rot_en_h: maximum input width when the input rotator is used + * @in_rot_dis_w: maximum input width when the input rotator is used + * @out_rot_en_w: maximum output width for the output rotator enabled + * @out_rot_dis_w: maximum output width for the output rotator enabled + */ +struct samsung_fimc_variant { + unsigned int pix_hoff:1; + unsigned int has_inp_rot:1; + unsigned int has_out_rot:1; + + u16 min_inp_pixsize; + u16 min_out_pixsize; + u16 scaler_en_w; + u16 scaler_dis_w; + u16 in_rot_en_h; + u16 in_rot_dis_w; + u16 out_rot_en_w; + u16 out_rot_dis_w; +}; + +/** + * struct samsung_fimc_driverdata - per-device type driver data for init time. + * + * @variant: the variant information for this driver. + * @dev_cnt: number of fimc sub-devices available in SoC + */ +struct samsung_fimc_driverdata { + struct samsung_fimc_variant *variant[FIMC_MAX_DEVS]; + int devs_cnt; +}; + +struct fimc_ctx; + +/** + * struct fimc_subdev - abstraction for a FIMC entity + * + * @slock: the spinlock protecting this data structure + * @lock: the mutex protecting this data structure + * @pdev: pointer to the FIMC platform device + * @id: FIMC device index (0..2) + * @clock[]: the clocks required for FIMC operation + * @regs: the mapped hardware registers + * @regs_res: the resource claimed for IO registers + * @irq: interrupt number of the FIMC subdevice + * @irqlock: spinlock protecting videbuffer queue + * @m2m: memory-to-memory V4L2 device information + * @state: the FIMC device state flags + */ +struct fimc_dev { + spinlock_t slock; + struct mutex lock; + struct platform_device *pdev; + struct samsung_fimc_variant *variant; + int id; + struct clk *clock[NUM_FIMC_CLOCKS]; + void __iomem *regs; + struct resource *regs_res; + int irq; + spinlock_t irqlock; + struct workqueue_struct *work_queue; + struct fimc_m2m_device m2m; + unsigned long state; +}; + +/** + * fimc_ctx - the device context data + * + * @lock: mutex protecting this data structure + * @s_frame: source frame properties + * @d_frame: destination frame properties + * @out_order_1p: output 1-plane YCBCR order + * @out_order_2p: output 2-plane YCBCR order + * @in_order_1p input 1-plane YCBCR order + * @in_order_2p: input 2-plane YCBCR order + * @in_path: input mode (DMA or camera) + * @out_path: output mode (DMA or FIFO) + * @scaler: image scaler properties + * @effect: image effect + * @rotation: image clockwise rotation in degrees + * @flip: image flip mode + * @flags: an additional flags for image conversion + * @state: flags to keep track of user configuration + * @fimc_dev: the FIMC device this context applies to + * @m2m_ctx: memory-to-memory device context + */ +struct fimc_ctx { + spinlock_t slock; + struct fimc_frame s_frame; + struct fimc_frame d_frame; + u32 out_order_1p; + u32 out_order_2p; + u32 in_order_1p; + u32 in_order_2p; + enum fimc_datapath in_path; + enum fimc_datapath out_path; + struct fimc_scaler scaler; + struct fimc_effect effect; + int rotation; + u32 flip; + u32 flags; + u32 state; + struct fimc_dev *fimc_dev; + struct v4l2_m2m_ctx *m2m_ctx; +}; + + +static inline int tiled_fmt(struct fimc_fmt *fmt) +{ + return 0; +} + +static inline void fimc_hw_clear_irq(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + S5P_CIGCTRL); + cfg |= S5P_CIGCTRL_IRQ_CLR; + writel(cfg, dev->regs + S5P_CIGCTRL); +} + +static inline void fimc_hw_start_scaler(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + S5P_CISCCTRL); + cfg |= S5P_CISCCTRL_SCALERSTART; + writel(cfg, dev->regs + S5P_CISCCTRL); +} + +static inline void fimc_hw_stop_scaler(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + S5P_CISCCTRL); + cfg &= ~S5P_CISCCTRL_SCALERSTART; + writel(cfg, dev->regs + S5P_CISCCTRL); +} + +static inline void fimc_hw_dis_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + S5P_CIIMGCPT); + cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + S5P_CIIMGCPT); +} + +static inline void fimc_hw_start_in_dma(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + S5P_MSCTRL); + cfg |= S5P_MSCTRL_ENVID; + writel(cfg, dev->regs + S5P_MSCTRL); +} + +static inline void fimc_hw_stop_in_dma(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + S5P_MSCTRL); + cfg &= ~S5P_MSCTRL_ENVID; + writel(cfg, dev->regs + S5P_MSCTRL); +} + +static inline struct fimc_frame *ctx_m2m_get_frame(struct fimc_ctx *ctx, + enum v4l2_buf_type type) +{ + struct fimc_frame *frame; + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) { + frame = &ctx->s_frame; + } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) { + frame = &ctx->d_frame; + } else { + v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, + "Wrong buffer/video queue type (%d)\n", type); + return ERR_PTR(-EINVAL); + } + + return frame; +} + +/* -----------------------------------------------------*/ +/* fimc-reg.c */ +void fimc_hw_reset(struct fimc_dev *dev); +void fimc_hw_set_rotation(struct fimc_ctx *ctx); +void fimc_hw_set_target_format(struct fimc_ctx *ctx); +void fimc_hw_set_out_dma(struct fimc_ctx *ctx); +void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable); +void fimc_hw_en_irq(struct fimc_dev *dev, int enable); +void fimc_hw_set_prescaler(struct fimc_ctx *ctx); +void fimc_hw_set_scaler(struct fimc_ctx *ctx); +void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_set_effect(struct fimc_ctx *ctx); +void fimc_hw_set_in_dma(struct fimc_ctx *ctx); +void fimc_hw_set_input_path(struct fimc_ctx *ctx); +void fimc_hw_set_output_path(struct fimc_ctx *ctx); +void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr); +void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr); + +#endif /* FIMC_CORE_H_ */ diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c new file mode 100644 index 00000000000..5570f1ce0c9 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -0,0 +1,527 @@ +/* + * Register interface file for Samsung Camera Interface (FIMC) driver + * + * Copyright (c) 2010 Samsung Electronics + * + * Sylwester Nawrocki, s.nawrocki@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/io.h> +#include <linux/delay.h> +#include <mach/map.h> + +#include "fimc-core.h" + + +void fimc_hw_reset(struct fimc_dev *dev) +{ + u32 cfg; + + cfg = readl(dev->regs + S5P_CISRCFMT); + cfg |= S5P_CISRCFMT_ITU601_8BIT; + writel(cfg, dev->regs + S5P_CISRCFMT); + + /* Software reset. */ + cfg = readl(dev->regs + S5P_CIGCTRL); + cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL); + writel(cfg, dev->regs + S5P_CIGCTRL); + msleep(1); + + cfg = readl(dev->regs + S5P_CIGCTRL); + cfg &= ~S5P_CIGCTRL_SWRST; + writel(cfg, dev->regs + S5P_CIGCTRL); + +} + +void fimc_hw_set_rotation(struct fimc_ctx *ctx) +{ + u32 cfg, flip; + struct fimc_dev *dev = ctx->fimc_dev; + + cfg = readl(dev->regs + S5P_CITRGFMT); + cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90); + + flip = readl(dev->regs + S5P_MSCTRL); + flip &= ~S5P_MSCTRL_FLIP_MASK; + + /* + * The input and output rotator cannot work simultaneously. + * Use the output rotator in output DMA mode or the input rotator + * in direct fifo output mode. + */ + if (ctx->rotation == 90 || ctx->rotation == 270) { + if (ctx->out_path == FIMC_LCDFIFO) { + cfg |= S5P_CITRGFMT_INROT90; + if (ctx->rotation == 270) + flip |= S5P_MSCTRL_FLIP_180; + } else { + cfg |= S5P_CITRGFMT_OUTROT90; + if (ctx->rotation == 270) + cfg |= S5P_CITRGFMT_FLIP_180; + } + } else if (ctx->rotation == 180) { + if (ctx->out_path == FIMC_LCDFIFO) + flip |= S5P_MSCTRL_FLIP_180; + else + cfg |= S5P_CITRGFMT_FLIP_180; + } + if (ctx->rotation == 180 || ctx->rotation == 270) + writel(flip, dev->regs + S5P_MSCTRL); + writel(cfg, dev->regs + S5P_CITRGFMT); +} + +static u32 fimc_hw_get_in_flip(u32 ctx_flip) +{ + u32 flip = S5P_MSCTRL_FLIP_NORMAL; + + switch (ctx_flip) { + case FLIP_X_AXIS: + flip = S5P_MSCTRL_FLIP_X_MIRROR; + break; + case FLIP_Y_AXIS: + flip = S5P_MSCTRL_FLIP_Y_MIRROR; + break; + case FLIP_XY_AXIS: + flip = S5P_MSCTRL_FLIP_180; + break; + } + + return flip; +} + +static u32 fimc_hw_get_target_flip(u32 ctx_flip) +{ + u32 flip = S5P_CITRGFMT_FLIP_NORMAL; + + switch (ctx_flip) { + case FLIP_X_AXIS: + flip = S5P_CITRGFMT_FLIP_X_MIRROR; + break; + case FLIP_Y_AXIS: + flip = S5P_CITRGFMT_FLIP_Y_MIRROR; + break; + case FLIP_XY_AXIS: + flip = S5P_CITRGFMT_FLIP_180; + break; + case FLIP_NONE: + break; + + } + return flip; +} + +void fimc_hw_set_target_format(struct fimc_ctx *ctx) +{ + u32 cfg; + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_frame *frame = &ctx->d_frame; + + dbg("w= %d, h= %d color: %d", frame->width, + frame->height, frame->fmt->color); + + cfg = readl(dev->regs + S5P_CITRGFMT); + cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK | + S5P_CITRGFMT_VSIZE_MASK); + + switch (frame->fmt->color) { + case S5P_FIMC_RGB565: + case S5P_FIMC_RGB666: + case S5P_FIMC_RGB888: + cfg |= S5P_CITRGFMT_RGB; + break; + case S5P_FIMC_YCBCR420: + cfg |= S5P_CITRGFMT_YCBCR420; + break; + case S5P_FIMC_YCBYCR422: + case S5P_FIMC_YCRYCB422: + case S5P_FIMC_CBYCRY422: + case S5P_FIMC_CRYCBY422: + if (frame->fmt->planes_cnt == 1) + cfg |= S5P_CITRGFMT_YCBCR422_1P; + else + cfg |= S5P_CITRGFMT_YCBCR422; + break; + default: + break; + } + + cfg |= S5P_CITRGFMT_HSIZE(frame->width); + cfg |= S5P_CITRGFMT_VSIZE(frame->height); + + if (ctx->rotation == 0) { + cfg &= ~S5P_CITRGFMT_FLIP_MASK; + cfg |= fimc_hw_get_target_flip(ctx->flip); + } + writel(cfg, dev->regs + S5P_CITRGFMT); + + cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK; + cfg |= (frame->width * frame->height); + writel(cfg, dev->regs + S5P_CITAREA); +} + +static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_frame *frame = &ctx->d_frame; + u32 cfg = 0; + + if (ctx->rotation == 90 || ctx->rotation == 270) { + cfg |= S5P_ORIG_SIZE_HOR(frame->f_height); + cfg |= S5P_ORIG_SIZE_VER(frame->f_width); + } else { + cfg |= S5P_ORIG_SIZE_HOR(frame->f_width); + cfg |= S5P_ORIG_SIZE_VER(frame->f_height); + } + writel(cfg, dev->regs + S5P_ORGOSIZE); +} + +void fimc_hw_set_out_dma(struct fimc_ctx *ctx) +{ + u32 cfg; + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_frame *frame = &ctx->d_frame; + struct fimc_dma_offset *offset = &frame->dma_offset; + + /* Set the input dma offsets. */ + cfg = 0; + cfg |= S5P_CIO_OFFS_HOR(offset->y_h); + cfg |= S5P_CIO_OFFS_VER(offset->y_v); + writel(cfg, dev->regs + S5P_CIOYOFF); + + cfg = 0; + cfg |= S5P_CIO_OFFS_HOR(offset->cb_h); + cfg |= S5P_CIO_OFFS_VER(offset->cb_v); + writel(cfg, dev->regs + S5P_CIOCBOFF); + + cfg = 0; + cfg |= S5P_CIO_OFFS_HOR(offset->cr_h); + cfg |= S5P_CIO_OFFS_VER(offset->cr_v); + writel(cfg, dev->regs + S5P_CIOCROFF); + + fimc_hw_set_out_dma_size(ctx); + + /* Configure chroma components order. */ + cfg = readl(dev->regs + S5P_CIOCTRL); + + cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK | + S5P_CIOCTRL_YCBCR_PLANE_MASK); + + if (frame->fmt->planes_cnt == 1) + cfg |= ctx->out_order_1p; + else if (frame->fmt->planes_cnt == 2) + cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE; + else if (frame->fmt->planes_cnt == 3) + cfg |= S5P_CIOCTRL_YCBCR_3PLANE; + + writel(cfg, dev->regs + S5P_CIOCTRL); +} + +static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable) +{ + u32 cfg = readl(dev->regs + S5P_ORGISIZE); + if (enable) + cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; + else + cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN; + writel(cfg, dev->regs + S5P_ORGISIZE); +} + +void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) +{ + unsigned long flags; + u32 cfg; + + spin_lock_irqsave(&dev->slock, flags); + + cfg = readl(dev->regs + S5P_CIOCTRL); + if (enable) + cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE; + else + cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE; + writel(cfg, dev->regs + S5P_CIOCTRL); + + spin_unlock_irqrestore(&dev->slock, flags); +} + +void fimc_hw_set_prescaler(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_scaler *sc = &ctx->scaler; + u32 cfg = 0, shfactor; + + shfactor = 10 - (sc->hfactor + sc->vfactor); + + cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor); + cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio); + cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio); + writel(cfg, dev->regs + S5P_CISCPRERATIO); + + cfg = 0; + cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width); + cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height); + writel(cfg, dev->regs + S5P_CISCPREDST); +} + +void fimc_hw_set_scaler(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_scaler *sc = &ctx->scaler; + struct fimc_frame *src_frame = &ctx->s_frame; + struct fimc_frame *dst_frame = &ctx->d_frame; + u32 cfg = 0; + + if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW)) + cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE); + + if (!sc->enabled) + cfg |= S5P_CISCCTRL_SCALERBYPASS; + + if (sc->scaleup_h) + cfg |= S5P_CISCCTRL_SCALEUP_H; + + if (sc->scaleup_v) + cfg |= S5P_CISCCTRL_SCALEUP_V; + + if (sc->copy_mode) + cfg |= S5P_CISCCTRL_ONE2ONE; + + + if (ctx->in_path == FIMC_DMA) { + if (src_frame->fmt->color == S5P_FIMC_RGB565) + cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565; + else if (src_frame->fmt->color == S5P_FIMC_RGB666) + cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666; + else if (src_frame->fmt->color == S5P_FIMC_RGB888) + cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888; + } + + if (ctx->out_path == FIMC_DMA) { + if (dst_frame->fmt->color == S5P_FIMC_RGB565) + cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565; + else if (dst_frame->fmt->color == S5P_FIMC_RGB666) + cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666; + else if (dst_frame->fmt->color == S5P_FIMC_RGB888) + cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; + } else { + cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; + + if (ctx->flags & FIMC_SCAN_MODE_INTERLACED) + cfg |= S5P_CISCCTRL_INTERLACE; + } + + dbg("main_hratio= 0x%X main_vratio= 0x%X", + sc->main_hratio, sc->main_vratio); + + cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio); + cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio); + + writel(cfg, dev->regs + S5P_CISCCTRL); +} + +void fimc_hw_en_capture(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + u32 cfg; + + cfg = readl(dev->regs + S5P_CIIMGCPT); + /* One shot mode for output DMA or freerun for FIFO. */ + if (ctx->out_path == FIMC_DMA) + cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE; + else + cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE; + + if (ctx->scaler.enabled) + cfg |= S5P_CIIMGCPT_IMGCPTEN_SC; + + writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT); +} + +void fimc_hw_set_effect(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_effect *effect = &ctx->effect; + u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER); + + cfg |= effect->type; + + if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) { + cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb); + cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr); + } + + writel(cfg, dev->regs + S5P_CIIMGEFF); +} + +static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_frame *frame = &ctx->s_frame; + u32 cfg_o = 0; + u32 cfg_r = 0; + + if (FIMC_LCDFIFO == ctx->out_path) + cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; + + cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width); + cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height); + cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width); + cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height); + + writel(cfg_o, dev->regs + S5P_ORGISIZE); + writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE); +} + +void fimc_hw_set_in_dma(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + struct fimc_frame *frame = &ctx->s_frame; + struct fimc_dma_offset *offset = &frame->dma_offset; + u32 cfg = 0; + + /* Set the pixel offsets. */ + cfg |= S5P_CIO_OFFS_HOR(offset->y_h); + cfg |= S5P_CIO_OFFS_VER(offset->y_v); + writel(cfg, dev->regs + S5P_CIIYOFF); + + cfg = 0; + cfg |= S5P_CIO_OFFS_HOR(offset->cb_h); + cfg |= S5P_CIO_OFFS_VER(offset->cb_v); + writel(cfg, dev->regs + S5P_CIICBOFF); + + cfg = 0; + cfg |= S5P_CIO_OFFS_HOR(offset->cr_h); + cfg |= S5P_CIO_OFFS_VER(offset->cr_v); + writel(cfg, dev->regs + S5P_CIICROFF); + + /* Input original and real size. */ + fimc_hw_set_in_dma_size(ctx); + + /* Autoload is used currently only in FIFO mode. */ + fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO); + + /* Set the input DMA to process single frame only. */ + cfg = readl(dev->regs + S5P_MSCTRL); + cfg &= ~(S5P_MSCTRL_FLIP_MASK + | S5P_MSCTRL_INFORMAT_MASK + | S5P_MSCTRL_IN_BURST_COUNT_MASK + | S5P_MSCTRL_INPUT_MASK + | S5P_MSCTRL_C_INT_IN_MASK + | S5P_MSCTRL_2P_IN_ORDER_MASK); + + cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY); + + switch (frame->fmt->color) { + case S5P_FIMC_RGB565: + case S5P_FIMC_RGB666: + case S5P_FIMC_RGB888: + cfg |= S5P_MSCTRL_INFORMAT_RGB; + break; + case S5P_FIMC_YCBCR420: + cfg |= S5P_MSCTRL_INFORMAT_YCBCR420; + + if (frame->fmt->planes_cnt == 2) + cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE; + else + cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; + + break; + case S5P_FIMC_YCBYCR422: + case S5P_FIMC_YCRYCB422: + case S5P_FIMC_CBYCRY422: + case S5P_FIMC_CRYCBY422: + if (frame->fmt->planes_cnt == 1) { + cfg |= ctx->in_order_1p + | S5P_MSCTRL_INFORMAT_YCBCR422_1P; + } else { + cfg |= S5P_MSCTRL_INFORMAT_YCBCR422; + + if (frame->fmt->planes_cnt == 2) + cfg |= ctx->in_order_2p + | S5P_MSCTRL_C_INT_IN_2PLANE; + else + cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; + } + break; + default: + break; + } + + /* + * Input DMA flip mode (and rotation). + * Do not allow simultaneous rotation and flipping. + */ + if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO) + cfg |= fimc_hw_get_in_flip(ctx->flip); + + writel(cfg, dev->regs + S5P_MSCTRL); + + /* Input/output DMA linear/tiled mode. */ + cfg = readl(dev->regs + S5P_CIDMAPARAM); + cfg &= ~S5P_CIDMAPARAM_TILE_MASK; + + if (tiled_fmt(ctx->s_frame.fmt)) + cfg |= S5P_CIDMAPARAM_R_64X32; + + if (tiled_fmt(ctx->d_frame.fmt)) + cfg |= S5P_CIDMAPARAM_W_64X32; + + writel(cfg, dev->regs + S5P_CIDMAPARAM); +} + + +void fimc_hw_set_input_path(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + + u32 cfg = readl(dev->regs + S5P_MSCTRL); + cfg &= ~S5P_MSCTRL_INPUT_MASK; + + if (ctx->in_path == FIMC_DMA) + cfg |= S5P_MSCTRL_INPUT_MEMORY; + else + cfg |= S5P_MSCTRL_INPUT_EXTCAM; + + writel(cfg, dev->regs + S5P_MSCTRL); +} + +void fimc_hw_set_output_path(struct fimc_ctx *ctx) +{ + struct fimc_dev *dev = ctx->fimc_dev; + + u32 cfg = readl(dev->regs + S5P_CISCCTRL); + cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO; + if (ctx->out_path == FIMC_LCDFIFO) + cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO; + writel(cfg, dev->regs + S5P_CISCCTRL); +} + +void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr) +{ + u32 cfg = 0; + + cfg = readl(dev->regs + S5P_CIREAL_ISIZE); + cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS; + writel(cfg, dev->regs + S5P_CIREAL_ISIZE); + + writel(paddr->y, dev->regs + S5P_CIIYSA0); + writel(paddr->cb, dev->regs + S5P_CIICBSA0); + writel(paddr->cr, dev->regs + S5P_CIICRSA0); + + cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS; + writel(cfg, dev->regs + S5P_CIREAL_ISIZE); +} + +void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr) +{ + int i; + /* Set all the output register sets to point to single video buffer. */ + for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) { + writel(paddr->y, dev->regs + S5P_CIOYSA(i)); + writel(paddr->cb, dev->regs + S5P_CIOCBSA(i)); + writel(paddr->cr, dev->regs + S5P_CIOCRSA(i)); + } +} diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h new file mode 100644 index 00000000000..a3cfe824db0 --- /dev/null +++ b/drivers/media/video/s5p-fimc/regs-fimc.h @@ -0,0 +1,293 @@ +/* + * Register definition file for Samsung Camera Interface (FIMC) driver + * + * Copyright (c) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef REGS_FIMC_H_ +#define REGS_FIMC_H_ + +#define S5P_CIOYSA(__x) (0x18 + (__x) * 4) +#define S5P_CIOCBSA(__x) (0x28 + (__x) * 4) +#define S5P_CIOCRSA(__x) (0x38 + (__x) * 4) + +/* Input source format */ +#define S5P_CISRCFMT 0x00 +#define S5P_CISRCFMT_ITU601_8BIT (1 << 31) +#define S5P_CISRCFMT_ITU601_16BIT (1 << 29) +#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14) +#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14) +#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14) +#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14) +#define S5P_CISRCFMT_HSIZE(x) ((x) << 16) +#define S5P_CISRCFMT_VSIZE(x) ((x) << 0) + +/* Window offset */ +#define S5P_CIWDOFST 0x04 +#define S5P_CIWDOFST_WINOFSEN (1 << 31) +#define S5P_CIWDOFST_CLROVFIY (1 << 30) +#define S5P_CIWDOFST_CLROVRLB (1 << 29) +#define S5P_CIWDOFST_WINHOROFST_MASK (0x7ff << 16) +#define S5P_CIWDOFST_CLROVFICB (1 << 15) +#define S5P_CIWDOFST_CLROVFICR (1 << 14) +#define S5P_CIWDOFST_WINHOROFST(x) ((x) << 16) +#define S5P_CIWDOFST_WINVEROFST(x) ((x) << 0) +#define S5P_CIWDOFST_WINVEROFST_MASK (0xfff << 0) + +/* Global control */ +#define S5P_CIGCTRL 0x08 +#define S5P_CIGCTRL_SWRST (1 << 31) +#define S5P_CIGCTRL_CAMRST_A (1 << 30) +#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29) +#define S5P_CIGCTRL_SELCAM_ITU_MASK (1 << 29) +#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27) +#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27) +#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27) +#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27) +#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27) +#define S5P_CIGCTRL_TESTPAT_SHIFT (27) +#define S5P_CIGCTRL_INVPOLPCLK (1 << 26) +#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25) +#define S5P_CIGCTRL_INVPOLHREF (1 << 24) +#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22) +#define S5P_CIGCTRL_HREF_MASK (1 << 21) +#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20) +#define S5P_CIGCTRL_IRQ_CLR (1 << 19) +#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16) +#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12) +#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7) +#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6) +#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4) +#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3) +#define S5P_CIGCTRL_INTERLACE (1 << 0) + +/* Window offset 2 */ +#define S5P_CIWDOFST2 0x14 +#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16) +#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0) +#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16) +#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0) + +/* Output DMA Y plane start address */ +#define S5P_CIOYSA1 0x18 +#define S5P_CIOYSA2 0x1c +#define S5P_CIOYSA3 0x20 +#define S5P_CIOYSA4 0x24 + +/* Output DMA Cb plane start address */ +#define S5P_CIOCBSA1 0x28 +#define S5P_CIOCBSA2 0x2c +#define S5P_CIOCBSA3 0x30 +#define S5P_CIOCBSA4 0x34 + +/* Output DMA Cr plane start address */ +#define S5P_CIOCRSA1 0x38 +#define S5P_CIOCRSA2 0x3c +#define S5P_CIOCRSA3 0x40 +#define S5P_CIOCRSA4 0x44 + +/* Target image format */ +#define S5P_CITRGFMT 0x48 +#define S5P_CITRGFMT_INROT90 (1 << 31) +#define S5P_CITRGFMT_YCBCR420 (0 << 29) +#define S5P_CITRGFMT_YCBCR422 (1 << 29) +#define S5P_CITRGFMT_YCBCR422_1P (2 << 29) +#define S5P_CITRGFMT_RGB (3 << 29) +#define S5P_CITRGFMT_FMT_MASK (3 << 29) +#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16) +#define S5P_CITRGFMT_FLIP_SHIFT (14) +#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14) +#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14) +#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14) +#define S5P_CITRGFMT_FLIP_180 (3 << 14) +#define S5P_CITRGFMT_FLIP_MASK (3 << 14) +#define S5P_CITRGFMT_OUTROT90 (1 << 13) +#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0) +#define S5P_CITRGFMT_HSIZE(x) ((x) << 16) +#define S5P_CITRGFMT_VSIZE(x) ((x) << 0) + +/* Output DMA control */ +#define S5P_CIOCTRL 0x4c +#define S5P_CIOCTRL_ORDER422_MASK (3 << 0) +#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0) +#define S5P_CIOCTRL_ORDER422_YCRYCB (1 << 0) +#define S5P_CIOCTRL_ORDER422_CBYCRY (2 << 0) +#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0) +#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2) +#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3) +#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3) +#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) +#define S5P_CIOCTRL_ORDER2P_SHIFT (24) +#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24) +#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24) + +/* Pre-scaler control 1 */ +#define S5P_CISCPRERATIO 0x50 +#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28) +#define S5P_CISCPRERATIO_HOR(x) ((x) << 16) +#define S5P_CISCPRERATIO_VER(x) ((x) << 0) + +#define S5P_CISCPREDST 0x54 +#define S5P_CISCPREDST_WIDTH(x) ((x) << 16) +#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0) + +/* Main scaler control */ +#define S5P_CISCCTRL 0x58 +#define S5P_CISCCTRL_SCALERBYPASS (1 << 31) +#define S5P_CISCCTRL_SCALEUP_H (1 << 30) +#define S5P_CISCCTRL_SCALEUP_V (1 << 29) +#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28) +#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27) +#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26) +#define S5P_CISCCTRL_INTERLACE (1 << 25) +#define S5P_CISCCTRL_SCALERSTART (1 << 15) +#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) +#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) +#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) +#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13) +#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) +#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) +#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) +#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) +#define S5P_CISCCTRL_RGB_EXT (1 << 10) +#define S5P_CISCCTRL_ONE2ONE (1 << 9) +#define S5P_CISCCTRL_SC_HORRATIO(x) ((x) << 16) +#define S5P_CISCCTRL_SC_VERRATIO(x) ((x) << 0) + +/* Target area */ +#define S5P_CITAREA 0x5c +#define S5P_CITAREA_MASK 0x0fffffff + +/* General status */ +#define S5P_CISTATUS 0x64 +#define S5P_CISTATUS_OVFIY (1 << 31) +#define S5P_CISTATUS_OVFICB (1 << 30) +#define S5P_CISTATUS_OVFICR (1 << 29) +#define S5P_CISTATUS_VSYNC (1 << 28) +#define S5P_CISTATUS_WINOFF_EN (1 << 25) +#define S5P_CISTATUS_IMGCPT_EN (1 << 22) +#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21) +#define S5P_CISTATUS_VSYNC_A (1 << 20) +#define S5P_CISTATUS_VSYNC_B (1 << 19) +#define S5P_CISTATUS_OVRLB (1 << 18) +#define S5P_CISTATUS_FRAME_END (1 << 17) +#define S5P_CISTATUS_LASTCAPT_END (1 << 16) +#define S5P_CISTATUS_VVALID_A (1 << 15) +#define S5P_CISTATUS_VVALID_B (1 << 14) + +/* Image capture control */ +#define S5P_CIIMGCPT 0xc0 +#define S5P_CIIMGCPT_IMGCPTEN (1 << 31) +#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30) +#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) +#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) + +/* Frame capture sequence */ +#define S5P_CICPTSEQ 0xc4 + +/* Image effect */ +#define S5P_CIIMGEFF 0xd0 +#define S5P_CIIMGEFF_IE_DISABLE (0 << 30) +#define S5P_CIIMGEFF_IE_ENABLE (1 << 30) +#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29) +#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29) +#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26) +#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26) +#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26) +#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26) +#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26) +#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26) +#define S5P_CIIMGEFF_FIN_MASK (7 << 26) +#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) +#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13) +#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0) + +/* Input DMA Y/Cb/Cr plane start address 0 */ +#define S5P_CIIYSA0 0xd4 +#define S5P_CIICBSA0 0xd8 +#define S5P_CIICRSA0 0xdc + +/* Real input DMA image size */ +#define S5P_CIREAL_ISIZE 0xf8 +#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31) +#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30) +#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16) +#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0) + + +/* Input DMA control */ +#define S5P_MSCTRL 0xfc +#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24) +#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16) +#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16 +#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15) +#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15) +#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15) +#define S5P_MSCTRL_FLIP_SHIFT 13 +#define S5P_MSCTRL_FLIP_MASK (3 << 13) +#define S5P_MSCTRL_FLIP_NORMAL (0 << 13) +#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13) +#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13) +#define S5P_MSCTRL_FLIP_180 (3 << 13) +#define S5P_MSCTRL_ORDER422_SHIFT 4 +#define S5P_MSCTRL_ORDER422_CRYCBY (0 << 4) +#define S5P_MSCTRL_ORDER422_YCRYCB (1 << 4) +#define S5P_MSCTRL_ORDER422_CBYCRY (2 << 4) +#define S5P_MSCTRL_ORDER422_YCBYCR (3 << 4) +#define S5P_MSCTRL_ORDER422_MASK (3 << 4) +#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3) +#define S5P_MSCTRL_INPUT_MEMORY (1 << 3) +#define S5P_MSCTRL_INPUT_MASK (1 << 3) +#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1) +#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1) +#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1) +#define S5P_MSCTRL_INFORMAT_RGB (3 << 1) +#define S5P_MSCTRL_INFORMAT_MASK (3 << 1) +#define S5P_MSCTRL_ENVID (1 << 0) +#define S5P_MSCTRL_FRAME_COUNT(x) ((x) << 24) + +/* Input DMA Y/Cb/Cr plane start address 1 */ +#define S5P_CIIYSA1 0x144 +#define S5P_CIICBSA1 0x148 +#define S5P_CIICRSA1 0x14c + +/* Output DMA Y/Cb/Cr offset */ +#define S5P_CIOYOFF 0x168 +#define S5P_CIOCBOFF 0x16c +#define S5P_CIOCROFF 0x170 + +/* Input DMA Y/Cb/Cr offset */ +#define S5P_CIIYOFF 0x174 +#define S5P_CIICBOFF 0x178 +#define S5P_CIICROFF 0x17c + +#define S5P_CIO_OFFS_VER(x) ((x) << 16) +#define S5P_CIO_OFFS_HOR(x) ((x) << 0) + +/* Input DMA original image size */ +#define S5P_ORGISIZE 0x180 + +/* Output DMA original image size */ +#define S5P_ORGOSIZE 0x184 + +#define S5P_ORIG_SIZE_VER(x) ((x) << 16) +#define S5P_ORIG_SIZE_HOR(x) ((x) << 0) + +/* Real output DMA image size (extension register) */ +#define S5P_CIEXTEN 0x188 + +#define S5P_CIDMAPARAM 0x18c +#define S5P_CIDMAPARAM_R_LINEAR (0 << 29) +#define S5P_CIDMAPARAM_R_64X32 (3 << 29) +#define S5P_CIDMAPARAM_W_LINEAR (0 << 13) +#define S5P_CIDMAPARAM_W_64X32 (3 << 13) +#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13)) + +/* MIPI CSI image format */ +#define S5P_CSIIMGFMT 0x194 + +#endif /* REGS_FIMC_H_ */ diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 76da7436868..ee963f4d01b 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -45,6 +45,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/saa7115.h> @@ -65,16 +66,19 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); struct saa711x_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + struct { + /* chroma gain control cluster */ + struct v4l2_ctrl *agc; + struct v4l2_ctrl *gain; + }; + v4l2_std_id std; int input; int output; int enable; int radio; - int bright; - int contrast; - int hue; - int sat; - int chroma_agc; int width; int height; u32 ident; @@ -90,6 +94,11 @@ static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct saa711x_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; +} + /* ----------------------------------------------------------------------- */ static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) @@ -741,96 +750,53 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) return 0; } -static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct saa711x_state *state = to_state(sd); - u8 val; switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); - return -ERANGE; - } - - state->bright = ctrl->value; - saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright); - break; - - case V4L2_CID_CONTRAST: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); - return -ERANGE; - } - - state->contrast = ctrl->value; - saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast); - break; - - case V4L2_CID_SATURATION: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); - return -ERANGE; - } - - state->sat = ctrl->value; - saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat); - break; - - case V4L2_CID_HUE: - if (ctrl->value < -128 || ctrl->value > 127) { - v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); - return -ERANGE; - } - - state->hue = ctrl->value; - saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); - break; case V4L2_CID_CHROMA_AGC: - val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL); - state->chroma_agc = ctrl->value; - if (ctrl->value) - val &= 0x7f; - else - val |= 0x80; - saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val); + /* chroma gain cluster */ + if (state->agc->cur.val) + state->gain->cur.val = + saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; break; - case V4L2_CID_CHROMA_GAIN: - /* Chroma gain cannot be set when AGC is enabled */ - if (state->chroma_agc == 1) - return -EINVAL; - saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80); - break; - default: - return -EINVAL; } - return 0; } -static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = state->bright; + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); break; + case V4L2_CID_CONTRAST: - ctrl->value = state->contrast; + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); break; + case V4L2_CID_SATURATION: - ctrl->value = state->sat; + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); break; + case V4L2_CID_HUE: - ctrl->value = state->hue; + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); break; + case V4L2_CID_CHROMA_AGC: - ctrl->value = state->chroma_agc; - break; - case V4L2_CID_CHROMA_GAIN: - ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; + /* chroma gain cluster */ + if (state->agc->val) + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); + else + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); + v4l2_ctrl_activate(state->gain, !state->agc->val); break; + default: return -EINVAL; } @@ -1223,25 +1189,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - case V4L2_CID_CHROMA_AGC: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_CHROMA_GAIN: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48); - default: - return -EINVAL; - } -} - static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct saa711x_state *state = to_state(sd); @@ -1518,17 +1465,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd) break; } v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); return 0; } /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { + .s_ctrl = saa711x_s_ctrl, + .g_volatile_ctrl = saa711x_g_volatile_ctrl, +}; + static const struct v4l2_subdev_core_ops saa711x_core_ops = { .log_status = saa711x_log_status, .g_chip_ident = saa711x_g_chip_ident, - .g_ctrl = saa711x_g_ctrl, - .s_ctrl = saa711x_s_ctrl, - .queryctrl = saa711x_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = saa711x_s_std, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio, @@ -1579,8 +1536,9 @@ static int saa711x_probe(struct i2c_client *client, { struct saa711x_state *state; struct v4l2_subdev *sd; - int i; - char name[17]; + struct v4l2_ctrl_handler *hdl; + int i; + char name[17]; char chip_id; int autodetect = !id || id->driver_data == 1; @@ -1619,15 +1577,38 @@ static int saa711x_probe(struct i2c_client *client, return -ENOMEM; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &saa711x_ops); + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); + state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, + V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); + state->gain->is_volatile = 1; + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(state); + return err; + } + state->agc->flags |= V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_cluster(2, &state->agc); + state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; state->radio = 0; - state->bright = 128; - state->contrast = 64; - state->hue = 0; - state->sat = 64; - state->chroma_agc = 1; switch (chip_id) { case '1': state->ident = V4L2_IDENT_SAA7111; @@ -1675,6 +1656,7 @@ static int saa711x_probe(struct i2c_client *client, if (state->ident > V4L2_IDENT_SAA7111A) saa711x_writeregs(sd, saa7115_init_misc); saa711x_set_v4lstd(sd, V4L2_STD_NTSC); + v4l2_ctrl_handler_setup(hdl); v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), @@ -1689,6 +1671,7 @@ static int saa711x_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 22bfd62c955..fda005e0167 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -2,7 +2,7 @@ config VIDEO_SAA7134 tristate "Philips SAA7134 support" depends on VIDEO_DEV && PCI && I2C && INPUT select VIDEOBUF_DMA_SG - select VIDEO_IR + depends on VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index d3bd82ad010..10460fd3ce3 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -630,7 +630,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, /* release the old buffer */ if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -646,12 +646,12 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, return err; } - if (0 != (err = videobuf_sg_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { + if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { dsp_buffer_free(dev); return err; } if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -660,7 +660,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, dev->dmasound.dma.sglen, 0))) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -669,7 +669,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, byte, but it doesn't work. So I allocate the DMA using the V4L functions, and force ALSA to use that as the DMA area */ - substream->runtime->dma_area = dev->dmasound.dma.vmalloc; + substream->runtime->dma_area = dev->dmasound.dma.vaddr; substream->runtime->dma_bytes = dev->dmasound.bufsize; substream->runtime->dma_addr = 0; @@ -696,7 +696,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -1080,7 +1080,7 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) /* Card "creation" */ card->private_free = snd_saa7134_free; - chip = (snd_card_saa7134_t *) card->private_data; + chip = card->private_data; spin_lock_init(&chip->lock); spin_lock_init(&chip->mixer_lock); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 07f6bb8ef9d..ec697fcd406 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5462,6 +5462,30 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_TECHNOTREND_BUDGET_T3000] = { + .name = "TechoTrend TT-budget T-3000", + .tuner_type = TUNER_PHILIPS_TD1316, + .audio_clock = 0x00187de7, + .radio_type = UNSET, + .tuner_addr = 0x63, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, }; @@ -6631,6 +6655,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x6655, .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x13c2, + .subdevice = 0x2804, + .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -7320,6 +7350,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: case SAA7134_BOARD_ASUS_EUROPA_HYBRID: + case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: { /* The Philips EUROPA based hybrid boards have the tuner diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 90f23188129..40bc635e8a3 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -256,7 +256,7 @@ void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); buf->vb.state = VIDEOBUF_NEEDS_INIT; } diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 31e82be1b7e..f26fe7661a1 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -481,6 +481,17 @@ static struct tda1004x_config medion_cardbus = { .request_firmware = philips_tda1004x_request_firmware }; +static struct tda1004x_config technotrend_budget_t3000_config = { + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .tuner_address = 0x63, + .request_firmware = philips_tda1004x_request_firmware +}; + /* ------------------------------------------------------------------ * tda 1004x based cards with philips silicon tuner */ @@ -1168,6 +1179,18 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; } break; + case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &technotrend_budget_t3000_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; + fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; + fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; + fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: fe0->dvb.frontend = dvb_attach(tda10046_attach, &philips_tu1216_61_config, diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 756a1ca8833..c040a180854 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -304,6 +304,7 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_H7 178 #define SAA7134_BOARD_BEHOLD_A7 179 #define SAA7134_BOARD_AVERMEDIA_M733A 180 +#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index 78d69950c00..45f8bfc1342 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -38,6 +38,7 @@ #include <linux/videodev2.h> #include <linux/i2c.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); @@ -55,14 +56,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); struct saa717x_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; v4l2_std_id std; int input; int enable; int radio; - int bright; - int contrast; - int hue; - int sat; int playback; int audio; int tuner_audio_mode; @@ -81,6 +79,11 @@ static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct saa717x_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; +} + /* ----------------------------------------------------------------------- */ /* for audio mode */ @@ -774,29 +777,6 @@ static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); } -/* write regs to video output level (bright,contrast,hue,sat) */ -static void set_video_output_level_regs(struct v4l2_subdev *sd, - struct saa717x_state *decoder) -{ - /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */ - saa717x_write(sd, 0x10a, decoder->bright); - - /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) - - 0h (luminance off) 40: i2c dump - c0h (-1.0 inverse chrominance) - 80h (-2.0 inverse chrominance) */ - saa717x_write(sd, 0x10b, decoder->contrast); - - /* saturation? 7fh(max)-40h(ITU)-0h(color off) - c0h (-1.0 inverse chrominance) - 80h (-2.0 inverse chrominance) */ - saa717x_write(sd, 0x10c, decoder->sat); - - /* color hue (phase) control - 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */ - saa717x_write(sd, 0x10d, decoder->hue); -} - /* write regs to set audio volume, bass and treble */ static int set_audio_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) @@ -829,9 +809,9 @@ static int set_audio_regs(struct v4l2_subdev *sd, saa717x_write(sd, 0x480, val); - /* bass and treble; go to another function */ /* set bass and treble */ - val = decoder->audio_main_bass | (decoder->audio_main_treble << 8); + val = decoder->audio_main_bass & 0x1f; + val |= (decoder->audio_main_treble & 0x1f) << 5; saa717x_write(sd, 0x488, val); return 0; } @@ -893,218 +873,55 @@ static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) saa717x_write(sd, 0x71 + task_shift, yscale >> 8); } -static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct saa717x_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value < 0 || ctrl->value > 255) { - v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); - return -ERANGE; - } - - state->bright = ctrl->value; - v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright); - saa717x_write(sd, 0x10a, state->bright); - break; - - case V4L2_CID_CONTRAST: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); - return -ERANGE; - } - - state->contrast = ctrl->value; - v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast); - saa717x_write(sd, 0x10b, state->contrast); - break; - - case V4L2_CID_SATURATION: - if (ctrl->value < 0 || ctrl->value > 127) { - v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); - return -ERANGE; - } - - state->sat = ctrl->value; - v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat); - saa717x_write(sd, 0x10c, state->sat); - break; - - case V4L2_CID_HUE: - if (ctrl->value < -128 || ctrl->value > 127) { - v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); - return -ERANGE; - } - - state->hue = ctrl->value; - v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue); - saa717x_write(sd, 0x10d, state->hue); - break; - - case V4L2_CID_AUDIO_MUTE: - state->audio_main_mute = ctrl->value; - set_audio_regs(sd, state); - break; - - case V4L2_CID_AUDIO_VOLUME: - state->audio_main_volume = ctrl->value; - set_audio_regs(sd, state); - break; - - case V4L2_CID_AUDIO_BALANCE: - state->audio_main_balance = ctrl->value; - set_audio_regs(sd, state); - break; - - case V4L2_CID_AUDIO_TREBLE: - state->audio_main_treble = ctrl->value; - set_audio_regs(sd, state); - break; - - case V4L2_CID_AUDIO_BASS: - state->audio_main_bass = ctrl->value; - set_audio_regs(sd, state); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = state->bright; - break; + saa717x_write(sd, 0x10a, ctrl->val); + return 0; case V4L2_CID_CONTRAST: - ctrl->value = state->contrast; - break; + saa717x_write(sd, 0x10b, ctrl->val); + return 0; case V4L2_CID_SATURATION: - ctrl->value = state->sat; - break; + saa717x_write(sd, 0x10c, ctrl->val); + return 0; case V4L2_CID_HUE: - ctrl->value = state->hue; - break; + saa717x_write(sd, 0x10d, ctrl->val); + return 0; case V4L2_CID_AUDIO_MUTE: - ctrl->value = state->audio_main_mute; + state->audio_main_mute = ctrl->val; break; case V4L2_CID_AUDIO_VOLUME: - ctrl->value = state->audio_main_volume; + state->audio_main_volume = ctrl->val; break; case V4L2_CID_AUDIO_BALANCE: - ctrl->value = state->audio_main_balance; + state->audio_main_balance = ctrl->val; break; case V4L2_CID_AUDIO_TREBLE: - ctrl->value = state->audio_main_treble; + state->audio_main_treble = ctrl->val; break; case V4L2_CID_AUDIO_BASS: - ctrl->value = state->audio_main_bass; + state->audio_main_bass = ctrl->val; break; default: - return -EINVAL; + return 0; } - + set_audio_regs(sd, state); return 0; } -static struct v4l2_queryctrl saa717x_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 64, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 64, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535 / 100, - .default_value = 58880, - .flags = 0, - }, { - .id = V4L2_CID_AUDIO_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Balance", - .minimum = 0, - .maximum = 65535, - .step = 65535 / 100, - .default_value = 32768, - .flags = 0, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - }, { - .id = V4L2_CID_AUDIO_BASS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Bass", - .minimum = 0, - .maximum = 65535, - .step = 65535 / 100, - .default_value = 32768, - }, { - .id = V4L2_CID_AUDIO_TREBLE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Treble", - .minimum = 0, - .maximum = 65535, - .step = 65535 / 100, - .default_value = 32768, - }, -}; - static int saa717x_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -1158,18 +975,6 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd, return 0; } -static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) - if (qc->id && qc->id == saa717x_qctrl[i].id) { - memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); - return 0; - } - return -EINVAL; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { @@ -1386,17 +1191,34 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } +static int saa717x_log_status(struct v4l2_subdev *sd) +{ + struct saa717x_state *state = to_state(sd); + + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); + return 0; +} + /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { + .s_ctrl = saa717x_s_ctrl, +}; + static const struct v4l2_subdev_core_ops saa717x_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = saa717x_g_register, .s_register = saa717x_s_register, #endif - .queryctrl = saa717x_queryctrl, - .g_ctrl = saa717x_g_ctrl, - .s_ctrl = saa717x_s_ctrl, .s_std = saa717x_s_std, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .log_status = saa717x_log_status, }; static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { @@ -1432,6 +1254,7 @@ static int saa717x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct saa717x_state *decoder; + struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; u8 id = 0; char *p = ""; @@ -1467,16 +1290,41 @@ static int saa717x_probe(struct i2c_client *client, p = "saa7171"; v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, client->addr << 1, client->adapter->name); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 9); + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 68); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 64); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_BASS, -16, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(decoder); + return err; + } + decoder->std = V4L2_STD_NTSC; decoder->input = -1; decoder->enable = 1; - /* tune these parameters */ - decoder->bright = 0x80; - decoder->contrast = 0x44; - decoder->sat = 0x40; - decoder->hue = 0x00; - /* FIXME!! */ decoder->playback = 0; /* initially capture mode used */ decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ @@ -1487,23 +1335,13 @@ static int saa717x_probe(struct i2c_client *client, /* set volume, bass and treble */ decoder->audio_main_vol_l = 6; decoder->audio_main_vol_r = 6; - decoder->audio_main_bass = 0; - decoder->audio_main_treble = 0; - decoder->audio_main_mute = 0; - decoder->audio_main_balance = 32768; - /* normalize (24 to -40 (not -84) -> 65535 to 0) */ - decoder->audio_main_volume = - (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40)); v4l2_dbg(1, debug, sd, "writing init values\n"); /* FIXME!! */ saa717x_write_regs(sd, reg_init_initialize); - set_video_output_level_regs(sd, decoder); - /* set bass,treble to 0db 20041101 K.Ohta */ - decoder->audio_main_bass = 0; - decoder->audio_main_treble = 0; - set_audio_regs(sd, decoder); + + v4l2_ctrl_handler_setup(hdl); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(2*HZ); @@ -1515,6 +1353,7 @@ static int saa717x_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 961bfa2fea9..2b24bd0de3a 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -633,6 +633,12 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) cdwdr_width *= 2; } + /* CSI2 special configuration */ + if (pcdev->pdata->csi2_dev) { + in_width = ((in_width - 2) * 2); + left_offset *= 2; + } + /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ camor = left_offset | (top_offset << 16); @@ -743,16 +749,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: switch (cam->code) { - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ break; - case V4L2_MBUS_FMT_YVYU8_2X8_BE: + case V4L2_MBUS_FMT_VYUY8_2X8: value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */ break; - case V4L2_MBUS_FMT_YUYV8_2X8_LE: + case V4L2_MBUS_FMT_YUYV8_2X8: value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */ break; - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_YVYU8_2X8: value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */ break; default: @@ -767,6 +773,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; value |= pcdev->is_16bit ? 1 << 12 : 0; + + /* CSI2 mode */ + if (pcdev->pdata->csi2_dev) + value |= 3 << 12; + ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); @@ -883,6 +894,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); + struct sh_mobile_ceu_dev *pcdev = ici->priv; int ret, k, n; int formats = 0; struct sh_mobile_ceu_cam *cam; @@ -896,19 +909,19 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_err(icd->dev.parent, - "Invalid format code #%u: %d\n", idx, code); + dev_err(dev, "Invalid format code #%u: %d\n", idx, code); return -EINVAL; } - ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; + if (!pcdev->pdata->csi2_dev) { + ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; + } if (!icd->host_priv) { struct v4l2_mbus_framefmt mf; struct v4l2_rect rect; - struct device *dev = icd->dev.parent; int shift = 0; /* FIXME: subwindow is lost between close / open */ @@ -927,7 +940,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int /* Try 2560x1920, 1280x960, 640x480, 320x240 */ mf.width = 2560 >> shift; mf.height = 1920 >> shift; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + s_mbus_fmt, &mf); if (ret < 0) return ret; shift++; @@ -965,10 +979,10 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int cam->extra_fmt = NULL; switch (code) { - case V4L2_MBUS_FMT_YUYV8_2X8_BE: - case V4L2_MBUS_FMT_YVYU8_2X8_BE: - case V4L2_MBUS_FMT_YUYV8_2X8_LE: - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_VYUY8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YVYU8_2X8: if (cam->extra_fmt) break; @@ -1005,7 +1019,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int xlate->code = code; xlate++; dev_dbg(dev, "Providing format %s in pass-through mode\n", - xlate->host_fmt->name); + fmt->name); } return formats; @@ -1228,7 +1242,8 @@ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_cropcap cap; int ret; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + s_mbus_fmt, mf); if (ret < 0) return ret; @@ -1257,7 +1272,8 @@ static int client_s_fmt(struct soc_camera_device *icd, tmp_h = min(2 * tmp_h, max_height); mf->width = tmp_w; mf->height = tmp_h; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + s_mbus_fmt, mf); dev_geo(dev, "Camera scaled to %ux%u\n", mf->width, mf->height); if (ret < 0) { @@ -1514,7 +1530,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct device *dev = icd->dev.parent; __u32 pixfmt = pix->pixelformat; const struct soc_camera_format_xlate *xlate; - unsigned int ceu_sub_width, ceu_sub_height; + /* Keep Compiler Happy */ + unsigned int ceu_sub_width = 0, ceu_sub_height = 0; u16 scale_v, scale_h; int ret; bool image_mode; @@ -1569,8 +1586,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, /* Done with the camera. Now see if we can improve the result */ - dev_geo(dev, "Camera %d fmt %ux%u, requested %ux%u\n", - ret, mf.width, mf.height, pix->width, pix->height); + dev_geo(dev, "fmt %ux%u, requested %ux%u\n", + mf.width, mf.height, pix->width, pix->height); if (ret < 0) return ret; @@ -1634,6 +1651,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, int width, height; int ret; + dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n", + pixfmt, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); @@ -1660,7 +1680,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, mf.code = xlate->code; mf.colorspace = pix->colorspace; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, try_mbus_fmt, &mf); if (ret < 0) return ret; @@ -1684,7 +1704,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, */ mf.width = 2560; mf.height = 1920; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + try_mbus_fmt, &mf); if (ret < 0) { /* Shouldn't actually happen... */ dev_err(icd->dev.parent, @@ -1699,6 +1720,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, pix->height = height; } + dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n", + __func__, ret, pix->pixelformat, pix->width, pix->height); + return ret; } @@ -1853,6 +1877,30 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; +struct bus_wait { + struct notifier_block notifier; + struct completion completion; + struct device *dev; +}; + +static int bus_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct bus_wait *wait = container_of(nb, struct bus_wait, notifier); + + if (wait->dev != dev) + return NOTIFY_DONE; + + switch (action) { + case BUS_NOTIFY_UNBOUND_DRIVER: + /* Protect from module unloading */ + wait_for_completion(&wait->completion); + return NOTIFY_OK; + } + return NOTIFY_DONE; +} + static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) { struct sh_mobile_ceu_dev *pcdev; @@ -1860,6 +1908,11 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) void __iomem *base; unsigned int irq; int err = 0; + struct bus_wait wait = { + .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), + .notifier.notifier_call = bus_notify, + }; + struct device *csi2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -1931,12 +1984,54 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; + /* CSI2 interfacing */ + csi2 = pcdev->pdata->csi2_dev; + if (csi2) { + wait.dev = csi2; + + err = bus_register_notifier(&platform_bus_type, &wait.notifier); + if (err < 0) + goto exit_free_clk; + + /* + * From this point the driver module will not unload, until + * we complete the completion. + */ + + if (!csi2->driver || !csi2->driver->owner) { + complete(&wait.completion); + /* Either too late, or probing failed */ + bus_unregister_notifier(&platform_bus_type, &wait.notifier); + err = -ENXIO; + goto exit_free_clk; + } + + /* + * The module is still loaded, in the worst case it is hanging + * in device release on our completion. So, _now_ dereferencing + * the "owner" is safe! + */ + + err = try_module_get(csi2->driver->owner); + + /* Let notifier complete, if it has been locked */ + complete(&wait.completion); + bus_unregister_notifier(&platform_bus_type, &wait.notifier); + if (!err) { + err = -ENODEV; + goto exit_free_clk; + } + } + err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_free_clk; + goto exit_module_put; return 0; +exit_module_put: + if (csi2 && csi2->driver) + module_put(csi2->driver->owner); exit_free_clk: pm_runtime_disable(&pdev->dev); free_irq(pcdev->irq, pcdev); @@ -1956,6 +2051,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, struct sh_mobile_ceu_dev, ici); + struct device *csi2 = pcdev->pdata->csi2_dev; soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); @@ -1963,7 +2059,10 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); iounmap(pcdev->base); + if (csi2 && csi2->driver) + module_put(csi2->driver->owner); kfree(pcdev); + return 0; } @@ -1995,6 +2094,8 @@ static struct platform_driver sh_mobile_ceu_driver = { static int __init sh_mobile_ceu_init(void) { + /* Whatever return code */ + request_module("sh_mobile_csi2"); return platform_driver_register(&sh_mobile_ceu_driver); } diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c new file mode 100644 index 00000000000..84a64681931 --- /dev/null +++ b/drivers/media/video/sh_mobile_csi2.c @@ -0,0 +1,354 @@ +/* + * Driver for the SH-Mobile MIPI CSI-2 unit + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/sh_mobile_csi2.h> +#include <media/soc_camera.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define SH_CSI2_TREF 0x00 +#define SH_CSI2_SRST 0x04 +#define SH_CSI2_PHYCNT 0x08 +#define SH_CSI2_CHKSUM 0x0C +#define SH_CSI2_VCDT 0x10 + +struct sh_csi2 { + struct v4l2_subdev subdev; + struct list_head list; + struct notifier_block notifier; + unsigned int irq; + void __iomem *base; + struct platform_device *pdev; + struct sh_csi2_client_config *client; +}; + +static int sh_csi2_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + + if (mf->width > 8188) + mf->width = 8188; + else if (mf->width & 1) + mf->width &= ~1; + + switch (pdata->type) { + case SH_CSI2C: + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */ + case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */ + case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + break; + default: + /* All MIPI CSI-2 devices must support one of primary formats */ + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; + } + break; + case SH_CSI2I: + switch (mf->code) { + case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */ + case V4L2_MBUS_FMT_SBGGR12_1X12: /* RAW12 */ + break; + default: + /* All MIPI CSI-2 devices must support one of primary formats */ + mf->code = V4L2_MBUS_FMT_SBGGR8_1X8; + } + break; + } + + return 0; +} + +/* + * We have done our best in try_fmt to try and tell the sensor, which formats + * we support. If now the configuration is unsuitable for us we can only + * error out. + */ +static int sh_csi2_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + u32 tmp = (priv->client->channel & 3) << 8; + + dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); + if (mf->width > 8188 || mf->width & 1) + return -EINVAL; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + tmp |= 0x1e; /* YUV422 8 bit */ + break; + case V4L2_MBUS_FMT_YUYV8_1_5X8: + tmp |= 0x18; /* YUV420 8 bit */ + break; + case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE: + tmp |= 0x21; /* RGB555 */ + break; + case V4L2_MBUS_FMT_RGB565_2X8_BE: + tmp |= 0x22; /* RGB565 */ + break; + case V4L2_MBUS_FMT_GREY8_1X8: + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + tmp |= 0x2a; /* RAW8 */ + break; + default: + return -EINVAL; + } + + iowrite32(tmp, priv->base + SH_CSI2_VCDT); + + return 0; +} + +static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { + .s_mbus_fmt = sh_csi2_s_fmt, + .try_mbus_fmt = sh_csi2_try_fmt, +}; + +static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops; + +static struct v4l2_subdev_ops sh_csi2_subdev_ops = { + .core = &sh_csi2_subdev_core_ops, + .video = &sh_csi2_subdev_video_ops, +}; + +static void sh_csi2_hwinit(struct sh_csi2 *priv) +{ + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */ + + /* Reflect registers immediately */ + iowrite32(0x00000001, priv->base + SH_CSI2_TREF); + /* reset CSI2 harware */ + iowrite32(0x00000001, priv->base + SH_CSI2_SRST); + udelay(5); + iowrite32(0x00000000, priv->base + SH_CSI2_SRST); + + if (priv->client->lanes & 3) + tmp |= priv->client->lanes & 3; + else + /* Default - both lanes */ + tmp |= 3; + + if (priv->client->phy == SH_CSI2_PHY_MAIN) + tmp |= 0x8000; + + iowrite32(tmp, priv->base + SH_CSI2_PHYCNT); + + tmp = 0; + if (pdata->flags & SH_CSI2_ECC) + tmp |= 2; + if (pdata->flags & SH_CSI2_CRC) + tmp |= 1; + iowrite32(tmp, priv->base + SH_CSI2_CHKSUM); +} + +static int sh_csi2_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int sh_csi2_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent); + struct sh_csi2 *priv = + container_of(nb, struct sh_csi2, notifier); + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + int ret, i; + + for (i = 0; i < pdata->num_clients; i++) + if (&pdata->clients[i].pdev->dev == icd->pdev) + break; + + dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i); + + if (i == pdata->num_clients) + return NOTIFY_DONE; + + switch (action) { + case BUS_NOTIFY_BOUND_DRIVER: + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s", + dev_name(v4l2_dev->dev), ".mipi-csi"); + ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev); + dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); + if (ret < 0) + return NOTIFY_DONE; + + priv->client = pdata->clients + i; + + icd->ops->set_bus_param = sh_csi2_set_bus_param; + icd->ops->query_bus_param = sh_csi2_query_bus_param; + + pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev)); + + sh_csi2_hwinit(priv); + break; + case BUS_NOTIFY_UNBIND_DRIVER: + priv->client = NULL; + + /* Driver is about to be unbound */ + icd->ops->set_bus_param = NULL; + icd->ops->query_bus_param = NULL; + + v4l2_device_unregister_subdev(&priv->subdev); + + pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); + break; + } + + return NOTIFY_OK; +} + +static __devinit int sh_csi2_probe(struct platform_device *pdev) +{ + struct resource *res; + unsigned int irq; + int ret; + struct sh_csi2 *priv; + /* Platform data specify the PHY, lanes, ECC, CRC */ + struct sh_csi2_pdata *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* Interrupt unused so far */ + irq = platform_get_irq(pdev, 0); + + if (!res || (int)irq <= 0 || !pdata) { + dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); + return -ENODEV; + } + + /* TODO: Add support for CSI2I. Careful: different register layout! */ + if (pdata->type != SH_CSI2C) { + dev_err(&pdev->dev, "Only CSI2C supported ATM.\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->irq = irq; + priv->notifier.notifier_call = sh_csi2_notify; + + /* We MUST attach after the MIPI sensor */ + ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier); + if (ret < 0) { + dev_err(&pdev->dev, "CSI2 cannot register notifier\n"); + goto ernotify; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "CSI2 register region already claimed\n"); + ret = -EBUSY; + goto ereqreg; + } + + priv->base = ioremap(res->start, resource_size(res)); + if (!priv->base) { + ret = -ENXIO; + dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); + goto eremap; + } + + priv->pdev = pdev; + + v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); + + platform_set_drvdata(pdev, priv); + + pm_runtime_enable(&pdev->dev); + + dev_dbg(&pdev->dev, "CSI2 probed.\n"); + + return 0; + +eremap: + release_mem_region(res->start, resource_size(res)); +ereqreg: + bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); +ernotify: + kfree(priv); + + return ret; +} + +static __devexit int sh_csi2_remove(struct platform_device *pdev) +{ + struct sh_csi2 *priv = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); + pm_runtime_disable(&pdev->dev); + iounmap(priv->base); + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return 0; +} + +static struct platform_driver __refdata sh_csi2_pdrv = { + .remove = __devexit_p(sh_csi2_remove), + .driver = { + .name = "sh-mobile-csi2", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_csi2_init(void) +{ + return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe); +} + +static void __exit sh_csi2_exit(void) +{ + platform_driver_unregister(&sh_csi2_pdrv); +} + +module_init(sh_csi2_init); +module_exit(sh_csi2_exit); + +MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sh-mobile-csi2"); diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index f5b892a2a8e..d394187eb70 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -18,6 +18,7 @@ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> #include <linux/version.h> #include <linux/videodev2.h> @@ -57,7 +58,7 @@ enum sh_vou_status { }; #define VOU_MAX_IMAGE_WIDTH 720 -#define VOU_MAX_IMAGE_HEIGHT 480 +#define VOU_MAX_IMAGE_HEIGHT 576 struct sh_vou_device { struct v4l2_device v4l2_dev; @@ -527,20 +528,17 @@ struct sh_vou_geometry { static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) { /* The compiler cannot know, that best and idx will indeed be set */ - unsigned int best_err = UINT_MAX, best = 0, width_max, height_max; + unsigned int best_err = UINT_MAX, best = 0, img_height_max; int i, idx = 0; - if (std & V4L2_STD_525_60) { - width_max = 858; - height_max = 262; - } else { - width_max = 864; - height_max = 312; - } + if (std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; /* Image width must be a multiple of 4 */ v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &geo->in_height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + &geo->in_height, 0, img_height_max, 1, 0); /* Select scales to come as close as possible to the output image */ for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { @@ -573,7 +571,7 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) unsigned int found = geo->output.height * vou_scale_v_den[i] / vou_scale_v_num[i]; - if (found > VOU_MAX_IMAGE_HEIGHT) + if (found > img_height_max) /* scales increase */ break; @@ -597,15 +595,18 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) */ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { - unsigned int best_err = UINT_MAX, best, width_max, height_max; + unsigned int best_err = UINT_MAX, best, width_max, height_max, + img_height_max; int i, idx; if (std & V4L2_STD_525_60) { width_max = 858; height_max = 262 * 2; + img_height_max = 480; } else { width_max = 864; height_max = 312 * 2; + img_height_max = 576; } /* Select scales to come as close as possible to the output image */ @@ -644,7 +645,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) unsigned int found = geo->in_height * vou_scale_v_num[i] / vou_scale_v_den[i]; - if (found > VOU_MAX_IMAGE_HEIGHT) + if (found > img_height_max) /* scales increase */ break; @@ -673,11 +674,12 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct sh_vou_device *vou_dev = video_get_drvdata(vdev); struct v4l2_pix_format *pix = &fmt->fmt.pix; + unsigned int img_height_max; int pix_idx; struct sh_vou_geometry geo; struct v4l2_mbus_framefmt mbfmt = { /* Revisit: is this the correct code? */ - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .field = V4L2_FIELD_INTERLACED, .colorspace = V4L2_COLORSPACE_SMPTE170M, }; @@ -701,9 +703,14 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, if (pix_idx == ARRAY_SIZE(vou_fmt)) return -EINVAL; + if (vou_dev->std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; + /* Image width must be a multiple of 4 */ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + &pix->height, 0, img_height_max, 1, 0); geo.in_width = pix->width; geo.in_height = pix->height; @@ -724,8 +731,8 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, /* Sanity checks */ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || - (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT || - mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + (unsigned)mbfmt.height > img_height_max || + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8) return -EIO; if (mbfmt.width != geo.output.width || @@ -936,10 +943,11 @@ static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) struct sh_vou_geometry geo; struct v4l2_mbus_framefmt mbfmt = { /* Revisit: is this the correct code? */ - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .field = V4L2_FIELD_INTERLACED, .colorspace = V4L2_COLORSPACE_SMPTE170M, }; + unsigned int img_height_max; int ret; dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__, @@ -948,14 +956,19 @@ static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; + if (vou_dev->std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; + v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1, - &rect->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + &rect->height, 0, img_height_max, 1, 0); if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH) rect->left = VOU_MAX_IMAGE_WIDTH - rect->width; - if (rect->height + rect->top > VOU_MAX_IMAGE_HEIGHT) - rect->top = VOU_MAX_IMAGE_HEIGHT - rect->height; + if (rect->height + rect->top > img_height_max) + rect->top = img_height_max - rect->height; geo.output = *rect; geo.in_width = pix->width; @@ -980,8 +993,8 @@ static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) /* Sanity checks */ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || - (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT || - mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + (unsigned)mbfmt.height > img_height_max || + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8) return -EIO; geo.output.width = mbfmt.width; @@ -1329,13 +1342,13 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) rect->left = 0; rect->top = 0; rect->width = VOU_MAX_IMAGE_WIDTH; - rect->height = VOU_MAX_IMAGE_HEIGHT; + rect->height = 480; pix->width = VOU_MAX_IMAGE_WIDTH; - pix->height = VOU_MAX_IMAGE_HEIGHT; + pix->height = 480; pix->pixelformat = V4L2_PIX_FMT_YVYU; pix->field = V4L2_FIELD_NONE; pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2; - pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * VOU_MAX_IMAGE_HEIGHT; + pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; region = request_mem_region(reg_res->start, resource_size(reg_res), diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 475757bfd7b..a499cacec1f 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -779,9 +779,12 @@ static int soc_camera_s_crop(struct file *file, void *fh, ret = ici->ops->get_crop(icd, ¤t_crop); /* Prohibit window size change with initialised buffers */ - if (icf->vb_vidq.bufs[0] && !ret && - (a->c.width != current_crop.c.width || - a->c.height != current_crop.c.height)) { + if (ret < 0) { + dev_err(&icd->dev, + "S_CROP denied: getting current crop failed\n"); + } else if (icf->vb_vidq.bufs[0] && + (a->c.width != current_crop.c.width || + a->c.height != current_crop.c.height)) { dev_err(&icd->dev, "S_CROP denied: queue initialised and sizes differ\n"); ret = -EBUSY; @@ -1107,13 +1110,14 @@ static int soc_camera_resume(struct device *dev) return ret; } -static struct bus_type soc_camera_bus_type = { +struct bus_type soc_camera_bus_type = { .name = "soc-camera", .probe = soc_camera_probe, .remove = soc_camera_remove, .suspend = soc_camera_suspend, .resume = soc_camera_resume, }; +EXPORT_SYMBOL_GPL(soc_camera_bus_type); static struct device_driver ic_drv = { .name = "camera", diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index 248c986f098..bf406e89c99 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -56,8 +56,8 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd) return p->bus_param; } -static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); @@ -65,6 +65,7 @@ static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd, mf->height = p->format.height; mf->code = p->format.code; mf->colorspace = p->format.colorspace; + mf->field = p->format.field; return 0; } @@ -83,10 +84,45 @@ static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int ind return 0; } +static int soc_camera_platform_g_crop(struct v4l2_subdev *sd, + struct v4l2_crop *a) +{ + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + + a->c.left = 0; + a->c.top = 0; + a->c.width = p->format.width; + a->c.height = p->format.height; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int soc_camera_platform_cropcap(struct v4l2_subdev *sd, + struct v4l2_cropcap *a) +{ + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = p->format.width; + a->bounds.height = p->format.height; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, - .try_mbus_fmt = soc_camera_platform_try_fmt, .enum_mbus_fmt = soc_camera_platform_enum_fmt, + .cropcap = soc_camera_platform_cropcap, + .g_crop = soc_camera_platform_g_crop, + .try_mbus_fmt = soc_camera_platform_fill_fmt, + .g_mbus_fmt = soc_camera_platform_fill_fmt, + .s_mbus_fmt = soc_camera_platform_fill_fmt, }; static struct v4l2_subdev_ops platform_subdev_ops = { diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index 8b63b6545e7..91391214c68 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -18,28 +18,28 @@ #define MBUS_IDX(f) (V4L2_MBUS_FMT_ ## f - V4L2_MBUS_FMT_FIXED - 1) static const struct soc_mbus_pixelfmt mbus_fmt[] = { - [MBUS_IDX(YUYV8_2X8_LE)] = { + [MBUS_IDX(YUYV8_2X8)] = { .fourcc = V4L2_PIX_FMT_YUYV, .name = "YUYV", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YVYU8_2X8_LE)] = { + [MBUS_IDX(YVYU8_2X8)] = { .fourcc = V4L2_PIX_FMT_YVYU, .name = "YVYU", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YUYV8_2X8_BE)] = { + [MBUS_IDX(UYVY8_2X8)] = { .fourcc = V4L2_PIX_FMT_UYVY, .name = "UYVY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YVYU8_2X8_BE)] = { + [MBUS_IDX(VYUY8_2X8)] = { .fourcc = V4L2_PIX_FMT_VYUY, .name = "VYUY", .bits_per_sample = 8, diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c deleted file mode 100644 index 5938ad8702e..00000000000 --- a/drivers/media/video/stv680.c +++ /dev/null @@ -1,1565 +0,0 @@ -/* - * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) - * - * Thanks to STMicroelectronics for information on the usb commands, and - * to Steve Miller at STM for his help and encouragement while I was - * writing this driver. - * - * This driver is based heavily on the - * Endpoints (formerly known as AOX) se401 USB Camera Driver - * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) - * - * Still somewhat based on the Linux ov511 driver. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * History: - * ver 0.1 October, 2001. Initial attempt. - * - * ver 0.2 November, 2001. Fixed asbility to resize, added brightness - * function, made more stable (?) - * - * ver 0.21 Nov, 2001. Added gamma correction and white balance, - * due to Alexander Schwartz. Still trying to - * improve stablility. Moved stuff into stv680.h - * - * ver 0.22 Nov, 2001. Added sharpen function (by Michael Sweet, - * mike@easysw.com) from GIMP, also used in pencam. - * Simple, fast, good integer math routine. - * - * ver 0.23 Dec, 2001 (gkh) - * Took out sharpen function, ran code through - * Lindent, and did other minor tweaks to get - * things to work properly with 2.5.1 - * - * ver 0.24 Jan, 2002 (kjs) - * Fixed the problem with webcam crashing after - * two pictures. Changed the way pic is halved to - * improve quality. Got rid of green line around - * frame. Fix brightness reset when changing size - * bug. Adjusted gamma filters slightly. - * - * ver 0.25 Jan, 2002 (kjs) - * Fixed a bug in which the driver sometimes attempted - * to set to a non-supported size. This allowed - * gnomemeeting to work. - * Fixed proc entry removal bug. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> -#include <linux/pagemap.h> -#include <linux/errno.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <linux/usb.h> -#include <linux/mutex.h> - -#include "stv680.h" - -static int video_nr = -1; - -static int swapRGB; /* 0 = default for auto select */ - -/* 0 = default to allow auto select; -1 = swap never, +1 = swap always */ -static int swapRGB_on; - -static unsigned int debug; - -#define PDEBUG(level, fmt, args...) \ - do { \ - if (debug >= level) \ - printk(KERN_INFO KBUILD_MODNAME " [%s:%d] \n" fmt, \ - __func__, __LINE__ , ## args); \ - } while (0) - - -/* - * Version Information - */ -#define DRIVER_VERSION "v0.25" -#define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>" -#define DRIVER_DESC "STV0680 USB Camera Driver" - -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC (debug, "Debug enabled or not"); -module_param(swapRGB_on, int, 0); -MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never"); -module_param(video_nr, int, 0); - -/******************************************************************** - * - * Memory management - * - * This is a shameless copy from the USB-cpia driver (linux kernel - * version 2.3.29 or so, I have no idea what this code actually does ;). - * Actually it seems to be a copy of a shameless copy of the bttv-driver. - * Or that is a copy of a shameless copy of ... (To the powers: is there - * no generic kernel-function to do this sort of stuff?) - * - * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says - * there will be one, but apparentely not yet -jerdfelt - * - * So I copied it again for the ov511 driver -claudio - * - * Same for the se401 driver -Jeroen - * - * And the STV0680 driver - Kevin - ********************************************************************/ -static void *rvmalloc (unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32 (size); - if (!mem) - return NULL; - - memset (mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - return mem; -} - -static void rvfree (void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree (mem); -} - - -/********************************************************************* - * pencam read/write functions - ********************************************************************/ - -static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size) -{ - int ret = -1; - - switch (set) { - case 0: /* 0xc1 */ - ret = usb_control_msg (stv680->udev, - usb_rcvctrlpipe (stv680->udev, 0), - req, - (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), - value, 0, buffer, size, PENCAM_TIMEOUT); - break; - - case 1: /* 0x41 */ - ret = usb_control_msg (stv680->udev, - usb_sndctrlpipe (stv680->udev, 0), - req, - (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), - value, 0, buffer, size, PENCAM_TIMEOUT); - break; - - case 2: /* 0x80 */ - ret = usb_control_msg (stv680->udev, - usb_rcvctrlpipe (stv680->udev, 0), - req, - (USB_DIR_IN | USB_RECIP_DEVICE), - value, 0, buffer, size, PENCAM_TIMEOUT); - break; - - case 3: /* 0x40 */ - ret = usb_control_msg (stv680->udev, - usb_sndctrlpipe (stv680->udev, 0), - req, - (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), - value, 0, buffer, size, PENCAM_TIMEOUT); - break; - - } - if ((ret < 0) && (req != 0x0a)) { - PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret); - } - return ret; -} - -static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate) -{ - - if (configuration != dev->udev->actconfig->desc.bConfigurationValue - || usb_reset_configuration (dev->udev) < 0) { - PDEBUG (1, "STV(e): FAILED to reset configuration %i", configuration); - return -1; - } - if (usb_set_interface (dev->udev, interface, alternate) < 0) { - PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate); - return -1; - } - return 0; -} - -static int stv_stop_video (struct usb_stv *dev) -{ - int i; - unsigned char *buf; - - buf = kmalloc (40, GFP_KERNEL); - if (buf == NULL) { - PDEBUG (0, "STV(e): Out of (small buf) memory"); - return -1; - } - - /* this is a high priority command; it stops all lower order commands */ - if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) { - i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02); /* Get Last Error; 2 = busy */ - PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buf[0], buf[1]); - } else { - PDEBUG (1, "STV(i): Camera reset to idle mode."); - } - - if ((i = stv_set_config (dev, 1, 0, 0)) < 0) - PDEBUG (1, "STV(e): Reset config during exit failed"); - - /* get current mode */ - buf[0] = 0xf0; - if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08) /* get mode */ - PDEBUG (0, "STV(e): Stop_video: problem setting original mode"); - if (dev->origMode != buf[0]) { - memset (buf, 0, 8); - buf[0] = (unsigned char) dev->origMode; - if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) { - PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed"); - i = -1; - } - buf[0] = 0xf0; - i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08); - if ((i != 0x08) || (buf[0] != dev->origMode)) { - PDEBUG (0, "STV(e): camera NOT set to original resolution."); - i = -1; - } else - PDEBUG (0, "STV(i): Camera set to original resolution"); - } - /* origMode */ - kfree(buf); - return i; -} - -static int stv_set_video_mode (struct usb_stv *dev) -{ - int i, stop_video = 1; - unsigned char *buf; - - buf = kmalloc (40, GFP_KERNEL); - if (buf == NULL) { - PDEBUG (0, "STV(e): Out of (small buf) memory"); - return -1; - } - - if ((i = stv_set_config (dev, 1, 0, 0)) < 0) { - kfree(buf); - return i; - } - - i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12); - if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) { - PDEBUG (1, "STV(e): Could not get descriptor 0100."); - goto error; - } - - /* set alternate interface 1 */ - if ((i = stv_set_config (dev, 1, 0, 1)) < 0) - goto error; - - if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10) - goto error; - PDEBUG (1, "STV(i): Setting video mode."); - /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */ - if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) { - stop_video = 0; - goto error; - } - goto exit; - -error: - kfree(buf); - if (stop_video == 1) - stv_stop_video (dev); - return -1; - -exit: - kfree(buf); - return 0; -} - -static int stv_init (struct usb_stv *stv680) -{ - int i = 0; - unsigned char *buffer; - unsigned long int bufsize; - - buffer = kzalloc (40, GFP_KERNEL); - if (buffer == NULL) { - PDEBUG (0, "STV(e): Out of (small buf) memory"); - return -1; - } - udelay (100); - - /* set config 1, interface 0, alternate 0 */ - if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) { - kfree(buffer); - PDEBUG (0, "STV(e): set config 1,0,0 failed"); - return -1; - } - /* ping camera to be sure STV0680 is present */ - if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02) - goto error; - if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) { - PDEBUG (1, "STV(e): camera ping failed!!"); - goto error; - } - - /* get camera descriptor */ - if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09) - goto error; - i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22); - if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) { - PDEBUG (1, "STV(e): Could not get descriptor 0200."); - goto error; - } - if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02) - goto error; - if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24) - goto error; - if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) - goto error; - - stv680->SupportedModes = buffer[7]; - i = stv680->SupportedModes; - stv680->CIF = 0; - stv680->VGA = 0; - stv680->QVGA = 0; - if (i & 1) - stv680->CIF = 1; - if (i & 2) - stv680->VGA = 1; - if (i & 8) - stv680->QVGA = 1; - if (stv680->SupportedModes == 0) { - PDEBUG (0, "STV(e): There are NO supported STV680 modes!!"); - i = -1; - goto error; - } else { - if (stv680->CIF) - PDEBUG (0, "STV(i): CIF is supported"); - if (stv680->QVGA) - PDEBUG (0, "STV(i): QVGA is supported"); - } - /* FW rev, ASIC rev, sensor ID */ - PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]); - PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]); - PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4)); - - /* set alternate interface 1 */ - if ((i = stv_set_config (stv680, 1, 0, 1)) < 0) - goto error; - - if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) - goto error; - if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08) - goto error; - i = buffer[3]; - PDEBUG (0, "STV(i): Camera has %i pictures.", i); - - /* get current mode */ - if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08) - goto error; - stv680->origMode = buffer[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ - - /* This will attemp CIF mode, if supported. If not, set to QVGA */ - memset (buffer, 0, 8); - if (stv680->CIF) - buffer[0] = 0x00; - else if (stv680->QVGA) - buffer[0] = 0x03; - if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) { - PDEBUG (0, "STV(i): Set_Camera_Mode failed"); - i = -1; - goto error; - } - buffer[0] = 0xf0; - stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08); - if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) { - PDEBUG (0, "STV(e): Error setting camera video mode!"); - i = -1; - goto error; - } else { - if (buffer[0] == 0) { - stv680->VideoMode = 0x0000; - PDEBUG (0, "STV(i): Video Mode set to CIF"); - } - if (buffer[0] == 0x03) { - stv680->VideoMode = 0x0300; - PDEBUG (0, "STV(i): Video Mode set to QVGA"); - } - } - if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10) - goto error; - bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]); - stv680->cwidth = (buffer[4] << 8) | (buffer[5]); /* ->camera = 322, 356, 644 */ - stv680->cheight = (buffer[6] << 8) | (buffer[7]); /* ->camera = 242, 292, 484 */ - stv680->origGain = buffer[12]; - - goto exit; - -error: - i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02); /* Get Last Error */ - PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buffer[0], buffer[1]); - kfree(buffer); - return -1; - -exit: - kfree(buffer); - - /* video = 320x240, 352x288 */ - if (stv680->CIF == 1) { - stv680->maxwidth = 352; - stv680->maxheight = 288; - stv680->vwidth = 352; - stv680->vheight = 288; - } - if (stv680->QVGA == 1) { - stv680->maxwidth = 320; - stv680->maxheight = 240; - stv680->vwidth = 320; - stv680->vheight = 240; - } - - stv680->rawbufsize = bufsize; /* must be ./. by 8 */ - stv680->maxframesize = bufsize * 3; /* RGB size */ - PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight); - PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize); - - /* some default values */ - stv680->bulk_in_endpointAddr = 0x82; - stv680->dropped = 0; - stv680->error = 0; - stv680->framecount = 0; - stv680->readcount = 0; - stv680->streaming = 0; - /* bright, white, colour, hue, contrast are set by software, not in stv0680 */ - stv680->brightness = 32767; - stv680->chgbright = 0; - stv680->whiteness = 0; /* only for greyscale */ - stv680->colour = 32767; - stv680->contrast = 32767; - stv680->hue = 32767; - stv680->palette = STV_VIDEO_PALETTE; - stv680->depth = 24; /* rgb24 bits */ - if ((swapRGB_on == 0) && (swapRGB == 0)) - PDEBUG (1, "STV(i): swapRGB is (auto) OFF"); - else if ((swapRGB_on == 0) && (swapRGB == 1)) - PDEBUG (1, "STV(i): swapRGB is (auto) ON"); - else if (swapRGB_on == 1) - PDEBUG (1, "STV(i): swapRGB is (forced) ON"); - else if (swapRGB_on == -1) - PDEBUG (1, "STV(i): swapRGB is (forced) OFF"); - - if (stv_set_video_mode (stv680) < 0) { - PDEBUG (0, "STV(e): Could not set video mode in stv_init"); - return -1; - } - - return 0; -} - -/***************** last of pencam routines *******************/ - -/**************************************************************************** - * sysfs - ***************************************************************************/ -#define stv680_file(name, variable, field) \ -static ssize_t show_##name(struct device *class_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct video_device *vdev = to_video_device(class_dev); \ - struct usb_stv *stv = video_get_drvdata(vdev); \ - return sprintf(buf, field, stv->variable); \ -} \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); - -stv680_file(model, camera_name, "%s\n"); -stv680_file(in_use, user, "%d\n"); -stv680_file(streaming, streaming, "%d\n"); -stv680_file(palette, palette, "%i\n"); -stv680_file(frames_total, readcount, "%d\n"); -stv680_file(frames_read, framecount, "%d\n"); -stv680_file(packets_dropped, dropped, "%d\n"); -stv680_file(decoding_errors, error, "%d\n"); - -static int stv680_create_sysfs_files(struct video_device *vdev) -{ - int rc; - - rc = device_create_file(&vdev->dev, &dev_attr_model); - if (rc) goto err; - rc = device_create_file(&vdev->dev, &dev_attr_in_use); - if (rc) goto err_model; - rc = device_create_file(&vdev->dev, &dev_attr_streaming); - if (rc) goto err_inuse; - rc = device_create_file(&vdev->dev, &dev_attr_palette); - if (rc) goto err_stream; - rc = device_create_file(&vdev->dev, &dev_attr_frames_total); - if (rc) goto err_pal; - rc = device_create_file(&vdev->dev, &dev_attr_frames_read); - if (rc) goto err_framtot; - rc = device_create_file(&vdev->dev, &dev_attr_packets_dropped); - if (rc) goto err_framread; - rc = device_create_file(&vdev->dev, &dev_attr_decoding_errors); - if (rc) goto err_dropped; - - return 0; - -err_dropped: - device_remove_file(&vdev->dev, &dev_attr_packets_dropped); -err_framread: - device_remove_file(&vdev->dev, &dev_attr_frames_read); -err_framtot: - device_remove_file(&vdev->dev, &dev_attr_frames_total); -err_pal: - device_remove_file(&vdev->dev, &dev_attr_palette); -err_stream: - device_remove_file(&vdev->dev, &dev_attr_streaming); -err_inuse: - device_remove_file(&vdev->dev, &dev_attr_in_use); -err_model: - device_remove_file(&vdev->dev, &dev_attr_model); -err: - PDEBUG(0, "STV(e): Could not create sysfs files"); - return rc; -} - -static void stv680_remove_sysfs_files(struct video_device *vdev) -{ - device_remove_file(&vdev->dev, &dev_attr_model); - device_remove_file(&vdev->dev, &dev_attr_in_use); - device_remove_file(&vdev->dev, &dev_attr_streaming); - device_remove_file(&vdev->dev, &dev_attr_palette); - device_remove_file(&vdev->dev, &dev_attr_frames_total); - device_remove_file(&vdev->dev, &dev_attr_frames_read); - device_remove_file(&vdev->dev, &dev_attr_packets_dropped); - device_remove_file(&vdev->dev, &dev_attr_decoding_errors); -} - -/******************************************************************** - * Camera control - *******************************************************************/ - -static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p) -{ - /* This sets values for v4l interface. max/min = 65535/0 */ - - p->brightness = stv680->brightness; - p->whiteness = stv680->whiteness; /* greyscale */ - p->colour = stv680->colour; - p->contrast = stv680->contrast; - p->hue = stv680->hue; - p->palette = stv680->palette; - p->depth = stv680->depth; - return 0; -} - -static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p) -{ - /* See above stv680_get_pict */ - - if (p->palette != STV_VIDEO_PALETTE) { - PDEBUG (2, "STV(e): Palette set error in _set_pic"); - return 1; - } - - if (stv680->brightness != p->brightness) { - stv680->chgbright = 1; - stv680->brightness = p->brightness; - } - - stv680->whiteness = p->whiteness; /* greyscale */ - stv680->colour = p->colour; - stv680->contrast = p->contrast; - stv680->hue = p->hue; - stv680->palette = p->palette; - stv680->depth = p->depth; - - return 0; -} - -static void stv680_video_irq (struct urb *urb) -{ - struct usb_stv *stv680 = urb->context; - int length = urb->actual_length; - - if (length < stv680->rawbufsize) - PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length); - - /* ohoh... */ - if (!stv680->streaming) - return; - - if (!stv680->udev) { - PDEBUG (0, "STV(e): device vapourished in video_irq"); - return; - } - - /* 0 sized packets happen if we are to fast, but sometimes the camera - keeps sending them forever... - */ - if (length && !urb->status) { - stv680->nullpackets = 0; - switch (stv680->scratch[stv680->scratch_next].state) { - case BUFFER_READY: - case BUFFER_BUSY: - stv680->dropped++; - break; - - case BUFFER_UNUSED: - memcpy (stv680->scratch[stv680->scratch_next].data, - (unsigned char *) urb->transfer_buffer, length); - stv680->scratch[stv680->scratch_next].state = BUFFER_READY; - stv680->scratch[stv680->scratch_next].length = length; - if (waitqueue_active (&stv680->wq)) { - wake_up_interruptible (&stv680->wq); - } - stv680->scratch_overflow = 0; - stv680->scratch_next++; - if (stv680->scratch_next >= STV680_NUMSCRATCH) - stv680->scratch_next = 0; - break; - } /* switch */ - } else { - stv680->nullpackets++; - if (stv680->nullpackets > STV680_MAX_NULLPACKETS) { - if (waitqueue_active (&stv680->wq)) { - wake_up_interruptible (&stv680->wq); - } - } - } /* if - else */ - - /* Resubmit urb for new data */ - urb->status = 0; - urb->dev = stv680->udev; - if (usb_submit_urb (urb, GFP_ATOMIC)) - PDEBUG (0, "STV(e): urb burned down in video irq"); - return; -} /* _video_irq */ - -static int stv680_start_stream (struct usb_stv *stv680) -{ - struct urb *urb; - int err = 0, i; - - stv680->streaming = 1; - - /* Do some memory allocation */ - for (i = 0; i < STV680_NUMFRAMES; i++) { - stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize; - stv680->frame[i].curpix = 0; - } - /* packet size = 4096 */ - for (i = 0; i < STV680_NUMSBUF; i++) { - stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); - if (stv680->sbuf[i].data == NULL) { - PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i); - goto nomem_err; - } - } - - stv680->scratch_next = 0; - stv680->scratch_use = 0; - stv680->scratch_overflow = 0; - for (i = 0; i < STV680_NUMSCRATCH; i++) { - stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); - if (stv680->scratch[i].data == NULL) { - PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i); - goto nomem_err; - } - stv680->scratch[i].state = BUFFER_UNUSED; - } - - for (i = 0; i < STV680_NUMSBUF; i++) { - urb = usb_alloc_urb (0, GFP_KERNEL); - if (!urb) - goto nomem_err; - - /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */ - usb_fill_bulk_urb (urb, stv680->udev, - usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr), - stv680->sbuf[i].data, stv680->rawbufsize, - stv680_video_irq, stv680); - stv680->urb[i] = urb; - err = usb_submit_urb (stv680->urb[i], GFP_KERNEL); - if (err) { - PDEBUG (0, "STV(e): urb burned down with err " - "%d in start stream %d", err, i); - goto nomem_err; - } - } /* i STV680_NUMSBUF */ - - stv680->framecount = 0; - return 0; - - nomem_err: - for (i = 0; i < STV680_NUMSBUF; i++) { - usb_kill_urb(stv680->urb[i]); - usb_free_urb(stv680->urb[i]); - stv680->urb[i] = NULL; - kfree(stv680->sbuf[i].data); - stv680->sbuf[i].data = NULL; - } - /* used in irq, free only as all URBs are dead */ - for (i = 0; i < STV680_NUMSCRATCH; i++) { - kfree(stv680->scratch[i].data); - stv680->scratch[i].data = NULL; - } - return -ENOMEM; - -} - -static int stv680_stop_stream (struct usb_stv *stv680) -{ - int i; - - if (!stv680->streaming || !stv680->udev) - return 1; - - stv680->streaming = 0; - - for (i = 0; i < STV680_NUMSBUF; i++) - if (stv680->urb[i]) { - usb_kill_urb (stv680->urb[i]); - usb_free_urb (stv680->urb[i]); - stv680->urb[i] = NULL; - kfree(stv680->sbuf[i].data); - } - for (i = 0; i < STV680_NUMSCRATCH; i++) { - kfree(stv680->scratch[i].data); - stv680->scratch[i].data = NULL; - } - - return 0; -} - -static int stv680_set_size (struct usb_stv *stv680, int width, int height) -{ - int wasstreaming = stv680->streaming; - - /* Check to see if we need to change */ - if ((stv680->vwidth == width) && (stv680->vheight == height)) - return 0; - - PDEBUG (1, "STV(i): size request for %i x %i", width, height); - /* Check for a valid mode */ - if ((!width || !height) || ((width & 1) || (height & 1))) { - PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight); - return 1; - } - - if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) { - width = stv680->maxwidth / 2; - height = stv680->maxheight / 2; - } else if ((width >= 158) && (width <= 166) && (stv680->QVGA == 1)) { - width = 160; - height = 120; - } else if ((width >= 172) && (width <= 180) && (stv680->CIF == 1)) { - width = 176; - height = 144; - } else if ((width >= 318) && (width <= 350) && (stv680->QVGA == 1)) { - width = 320; - height = 240; - } else if ((width >= 350) && (width <= 358) && (stv680->CIF == 1)) { - width = 352; - height = 288; - } else { - PDEBUG (1, "STV(e): request for non-supported size: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight); - return 1; - } - - /* Stop a current stream and start it again at the new size */ - if (wasstreaming) - stv680_stop_stream (stv680); - stv680->vwidth = width; - stv680->vheight = height; - PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight); - if (wasstreaming) - stv680_start_stream (stv680); - - return 0; -} - -/********************************************************************** - * Video Decoding - **********************************************************************/ - -/******* routines from the pencam program; hey, they work! ********/ - -/* - * STV0680 Vision Camera Chipset Driver - * Copyright (C) 2000 Adam Harrison <adam@antispin.org> -*/ - -#define RED 0 -#define GREEN 1 -#define BLUE 2 -#define AD(x, y, w) (((y)*(w)+(x))*3) - -static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer) -{ - int x, y, i; - int w = stv680->cwidth; - int vw = stv680->cwidth, vh = stv680->cheight; - unsigned int p = 0; - int colour = 0, bayer = 0; - unsigned char *raw = buffer->data; - struct stv680_frame *frame = &stv680->frame[stv680->curframe]; - unsigned char *output = frame->data; - unsigned char *temp = frame->data; - int offset = buffer->offset; - - if (frame->curpix == 0) { - if (frame->grabstate == FRAME_READY) { - frame->grabstate = FRAME_GRABBING; - } - } - if (offset != frame->curpix) { /* Regard frame as lost :( */ - frame->curpix = 0; - stv680->error++; - return; - } - - if ((stv680->vwidth == 320) || (stv680->vwidth == 160)) { - vw = 320; - vh = 240; - } - if ((stv680->vwidth == 352) || (stv680->vwidth == 176)) { - vw = 352; - vh = 288; - } - - memset (output, 0, 3 * vw * vh); /* clear output matrix. */ - - for (y = 0; y < vh; y++) { - for (x = 0; x < vw; x++) { - if (x & 1) - p = *(raw + y * w + (x >> 1)); - else - p = *(raw + y * w + (x >> 1) + (w >> 1)); - - if (y & 1) - bayer = 2; - else - bayer = 0; - if (x & 1) - bayer++; - - switch (bayer) { - case 0: - case 3: - colour = 1; - break; - case 1: - colour = 0; - break; - case 2: - colour = 2; - break; - } - i = (y * vw + x) * 3; - *(output + i + colour) = (unsigned char) p; - } /* for x */ - - } /* for y */ - - /****** gamma correction plus hardcoded white balance */ - /* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code. - Correction values red[], green[], blue[], are generated by - (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255. - White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and - converted to unsigned char. Values are in stv680.h */ - - for (y = 0; y < vh; y++) { - for (x = 0; x < vw; x++) { - i = (y * vw + x) * 3; - *(output + i) = red[*(output + i)]; - *(output + i + 1) = green[*(output + i + 1)]; - *(output + i + 2) = blue[*(output + i + 2)]; - } - } - - /****** bayer demosaic ******/ - for (y = 1; y < (vh - 1); y++) { - for (x = 1; x < (vw - 1); x++) { /* work out pixel type */ - if (y & 1) - bayer = 0; - else - bayer = 2; - if (!(x & 1)) - bayer++; - - switch (bayer) { - case 0: /* green. blue lr, red tb */ - *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y, vw) + BLUE) + (int) *(output + AD (x + 1, y, vw) + BLUE)) >> 1; - *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1; - break; - - case 1: /* blue. green lrtb, red diagonals */ - *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2; - *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2; - break; - - case 2: /* red. green lrtb, blue diagonals */ - *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2; - *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2; - break; - - case 3: /* green. red lr, blue tb */ - *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1; - *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1; - break; - } /* switch */ - } /* for x */ - } /* for y - end demosaic */ - - /* fix top and bottom row, left and right side */ - i = vw * 3; - memcpy (output, (output + i), i); - memcpy ((output + (vh * i)), (output + ((vh - 1) * i)), i); - for (y = 0; y < vh; y++) { - i = y * vw * 3; - memcpy ((output + i), (output + i + 3), 3); - memcpy ((output + i + (vw * 3)), (output + i + (vw - 1) * 3), 3); - } - - /* process all raw data, then trim to size if necessary */ - if ((stv680->vwidth == 160) || (stv680->vwidth == 176)) { - i = 0; - for (y = 0; y < vh; y++) { - if (!(y & 1)) { - for (x = 0; x < vw; x++) { - p = (y * vw + x) * 3; - if (!(x & 1)) { - *(output + i) = *(output + p); - *(output + i + 1) = *(output + p + 1); - *(output + i + 2) = *(output + p + 2); - i += 3; - } - } /* for x */ - } - } /* for y */ - } - /* reset to proper width */ - if ((stv680->vwidth == 160)) { - vw = 160; - vh = 120; - } - if ((stv680->vwidth == 176)) { - vw = 176; - vh = 144; - } - - /* output is RGB; some programs want BGR */ - /* swapRGB_on=0 -> program decides; swapRGB_on=1, always swap */ - /* swapRGB_on=-1, never swap */ - if (((swapRGB == 1) && (swapRGB_on != -1)) || (swapRGB_on == 1)) { - for (y = 0; y < vh; y++) { - for (x = 0; x < vw; x++) { - i = (y * vw + x) * 3; - *(temp) = *(output + i); - *(output + i) = *(output + i + 2); - *(output + i + 2) = *(temp); - } - } - } - /* brightness */ - if (stv680->chgbright == 1) { - if (stv680->brightness >= 32767) { - p = (stv680->brightness - 32767) / 256; - for (x = 0; x < (vw * vh * 3); x++) { - if ((*(output + x) + (unsigned char) p) > 255) - *(output + x) = 255; - else - *(output + x) += (unsigned char) p; - } /* for */ - } else { - p = (32767 - stv680->brightness) / 256; - for (x = 0; x < (vw * vh * 3); x++) { - if ((unsigned char) p > *(output + x)) - *(output + x) = 0; - else - *(output + x) -= (unsigned char) p; - } /* for */ - } /* else */ - } - /* if */ - frame->curpix = 0; - frame->curlinepix = 0; - frame->grabstate = FRAME_DONE; - stv680->framecount++; - stv680->readcount++; - if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) { - stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1); - } - -} /* bayer_unshuffle */ - -/******* end routines from the pencam program *********/ - -static int stv680_newframe (struct usb_stv *stv680, int framenr) -{ - int errors = 0; - - while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) { - if (!stv680->frame[framenr].curpix) { - errors++; - } - wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY)); - - if (stv680->nullpackets > STV680_MAX_NULLPACKETS) { - stv680->nullpackets = 0; - PDEBUG (2, "STV(i): too many null length packets, restarting capture"); - stv680_stop_stream (stv680); - stv680_start_stream (stv680); - } else { - if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) { - stv680->frame[framenr].grabstate = FRAME_ERROR; - PDEBUG (2, "STV(e): FRAME_ERROR in _newframe"); - return -EIO; - } - stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY; - - bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]); - - stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED; - stv680->scratch_use++; - if (stv680->scratch_use >= STV680_NUMSCRATCH) - stv680->scratch_use = 0; - if (errors > STV680_MAX_ERRORS) { - errors = 0; - PDEBUG (2, "STV(i): too many errors, restarting capture"); - stv680_stop_stream (stv680); - stv680_start_stream (stv680); - } - } /* else */ - } /* while */ - return 0; -} - -/********************************************************************* - * Video4Linux - *********************************************************************/ - -static int stv_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_stv *stv680 = video_get_drvdata(dev); - int err = 0; - - /* we are called with the BKL held */ - lock_kernel(); - stv680->user = 1; - err = stv_init (stv680); /* main initialization routine for camera */ - - if (err >= 0) { - stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES); - if (!stv680->fbuf) { - PDEBUG (0, "STV(e): Could not rvmalloc frame bufer"); - err = -ENOMEM; - } - file->private_data = dev; - } - if (err) - stv680->user = 0; - unlock_kernel(); - - return err; -} - -static int stv_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct usb_stv *stv680 = video_get_drvdata(dev); - int i; - - for (i = 0; i < STV680_NUMFRAMES; i++) - stv680->frame[i].grabstate = FRAME_UNUSED; - if (stv680->streaming) - stv680_stop_stream (stv680); - - if ((i = stv_stop_video (stv680)) < 0) - PDEBUG (1, "STV(e): stop_video failed in stv_close"); - - rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES); - stv680->user = 0; - - if (stv680->removed) { - kfree(stv680); - stv680 = NULL; - PDEBUG (0, "STV(i): device unregistered"); - } - file->private_data = NULL; - return 0; -} - -static long stv680_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct usb_stv *stv680 = video_get_drvdata(vdev); - - if (!stv680->udev) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP:{ - struct video_capability *b = arg; - - strcpy (b->name, stv680->camera_name); - b->type = VID_TYPE_CAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = stv680->maxwidth; - b->maxheight = stv680->maxheight; - b->minwidth = stv680->maxwidth / 2; - b->minheight = stv680->maxheight / 2; - return 0; - } - case VIDIOCGCHAN:{ - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy (v->name, "STV Camera"); - return 0; - } - case VIDIOCSCHAN:{ - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT:{ - struct video_picture *p = arg; - - stv680_get_pict (stv680, p); - return 0; - } - case VIDIOCSPICT:{ - struct video_picture *p = arg; - - if (stv680_set_pict (stv680, p)) - return -EINVAL; - return 0; - } - case VIDIOCSWIN:{ - struct video_window *vw = arg; - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (vw->width != stv680->vwidth) { - if (stv680_set_size (stv680, vw->width, vw->height)) { - PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN"); - return -EINVAL; - } - } - return 0; - } - case VIDIOCGWIN:{ - struct video_window *vw = arg; - - vw->x = 0; /* FIXME */ - vw->y = 0; - vw->chromakey = 0; - vw->flags = 0; - vw->clipcount = 0; - vw->width = stv680->vwidth; - vw->height = stv680->vheight; - return 0; - } - case VIDIOCGMBUF:{ - struct video_mbuf *vm = arg; - int i; - - memset (vm, 0, sizeof (*vm)); - vm->size = STV680_NUMFRAMES * stv680->maxframesize; - vm->frames = STV680_NUMFRAMES; - for (i = 0; i < STV680_NUMFRAMES; i++) - vm->offsets[i] = stv680->maxframesize * i; - return 0; - } - case VIDIOCMCAPTURE:{ - struct video_mmap *vm = arg; - - if (vm->format != STV_VIDEO_PALETTE) { - PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)", - vm->format, STV_VIDEO_PALETTE); - if ((vm->format == 3) && (swapRGB_on == 0)) { - PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON"); - /* this may fix those apps (e.g., xawtv) that want BGR */ - swapRGB = 1; - } - return -EINVAL; - } - if (vm->frame >= STV680_NUMFRAMES) { - PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES"); - return -EINVAL; - } - if ((stv680->frame[vm->frame].grabstate == FRAME_ERROR) - || (stv680->frame[vm->frame].grabstate == FRAME_GRABBING)) { - PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate (%i) error", - stv680->frame[vm->frame].grabstate); - return -EBUSY; - } - /* Is this according to the v4l spec??? */ - if (stv680->vwidth != vm->width) { - if (stv680_set_size (stv680, vm->width, vm->height)) { - PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed"); - return -EINVAL; - } - } - stv680->frame[vm->frame].grabstate = FRAME_READY; - - if (!stv680->streaming) - stv680_start_stream (stv680); - - return 0; - } - case VIDIOCSYNC:{ - int *frame = arg; - int ret = 0; - - if (*frame < 0 || *frame >= STV680_NUMFRAMES) { - PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC"); - return -EINVAL; - } - ret = stv680_newframe (stv680, *frame); - stv680->frame[*frame].grabstate = FRAME_UNUSED; - return ret; - } - case VIDIOCGFBUF:{ - struct video_buffer *vb = arg; - - memset (vb, 0, sizeof (*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - { - PDEBUG (2, "STV(e): VIDIOCCAPTURE failed"); - return -EINVAL; - } - case VIDIOCSFBUF: - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; - } /* end switch */ - - return 0; -} - -static long stv680_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, stv680_do_ioctl); -} - -static int stv680_mmap (struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - struct usb_stv *stv680 = video_get_drvdata(dev); - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - mutex_lock(&stv680->lock); - - if (stv680->udev == NULL) { - mutex_unlock(&stv680->lock); - return -EIO; - } - if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1) - & ~(PAGE_SIZE - 1))) { - mutex_unlock(&stv680->lock); - return -EINVAL; - } - pos = (unsigned long) stv680->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&stv680->lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - mutex_unlock(&stv680->lock); - - return 0; -} - -static ssize_t stv680_read (struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *dev = file->private_data; - unsigned long int realcount = count; - int ret = 0; - struct usb_stv *stv680 = video_get_drvdata(dev); - unsigned long int i; - - if (STV680_NUMFRAMES != 2) { - PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!"); - return -1; - } - if (stv680->udev == NULL) - return -EIO; - if (realcount > (stv680->vwidth * stv680->vheight * 3)) - realcount = stv680->vwidth * stv680->vheight * 3; - - /* Shouldn't happen: */ - if (stv680->frame[0].grabstate == FRAME_GRABBING) { - PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read"); - return -EBUSY; - } - stv680->frame[0].grabstate = FRAME_READY; - stv680->frame[1].grabstate = FRAME_UNUSED; - stv680->curframe = 0; - - if (!stv680->streaming) - stv680_start_stream (stv680); - - if (!stv680->streaming) { - ret = stv680_newframe (stv680, 0); /* ret should = 0 */ - } - - ret = stv680_newframe (stv680, 0); - - if (!ret) { - if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) { - PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i); - return -EFAULT; - } - } else { - realcount = ret; - } - stv680->frame[0].grabstate = FRAME_UNUSED; - return realcount; -} /* stv680_read */ - -static const struct v4l2_file_operations stv680_fops = { - .owner = THIS_MODULE, - .open = stv_open, - .release = stv_close, - .read = stv680_read, - .mmap = stv680_mmap, - .ioctl = stv680_ioctl, -}; -static struct video_device stv680_template = { - .name = "STV0680 USB camera", - .fops = &stv680_fops, - .release = video_device_release, -}; - -static int stv680_probe (struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_stv *stv680 = NULL; - char *camera_name = NULL; - int retval = 0; - - /* We don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) { - PDEBUG (0, "STV(e): Number of Configurations != 1"); - return -ENODEV; - } - - interface = &intf->altsetting[0]; - /* Is it a STV680? */ - if ((le16_to_cpu(dev->descriptor.idVendor) == USB_PENCAM_VENDOR_ID) && - (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) { - camera_name = "STV0680"; - PDEBUG (0, "STV(i): STV0680 camera found."); - } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) && - (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) { - camera_name = "Creative WebCam Go Mini"; - PDEBUG (0, "STV(i): Creative WebCam Go Mini found."); - } else { - PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values."); - PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer."); - retval = -ENODEV; - goto error; - } - /* We found one */ - if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) { - PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct."); - retval = -ENOMEM; - goto error; - } - - stv680->udev = dev; - stv680->camera_name = camera_name; - - stv680->vdev = video_device_alloc(); - if (!stv680->vdev) { - retval = -ENOMEM; - goto error; - } - memcpy(stv680->vdev, &stv680_template, sizeof(stv680_template)); - stv680->vdev->parent = &intf->dev; - video_set_drvdata(stv680->vdev, stv680); - - memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name)); - init_waitqueue_head (&stv680->wq); - mutex_init (&stv680->lock); - wmb (); - - if (video_register_device(stv680->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - PDEBUG (0, "STV(e): video_register_device failed"); - retval = -EIO; - goto error_vdev; - } - PDEBUG(0, "STV(i): registered new video device: %s", - video_device_node_name(stv680->vdev)); - - usb_set_intfdata (intf, stv680); - retval = stv680_create_sysfs_files(stv680->vdev); - if (retval) - goto error_unreg; - return 0; - -error_unreg: - video_unregister_device(stv680->vdev); -error_vdev: - video_device_release(stv680->vdev); -error: - kfree(stv680); - return retval; -} - -static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680) -{ - int i; - - stv680->udev = NULL; - stv680->frame[0].grabstate = FRAME_ERROR; - stv680->frame[1].grabstate = FRAME_ERROR; - stv680->streaming = 0; - - wake_up_interruptible (&stv680->wq); - - for (i = 0; i < STV680_NUMSBUF; i++) - if (stv680->urb[i]) { - usb_kill_urb (stv680->urb[i]); - usb_free_urb (stv680->urb[i]); - stv680->urb[i] = NULL; - kfree(stv680->sbuf[i].data); - } - for (i = 0; i < STV680_NUMSCRATCH; i++) - kfree(stv680->scratch[i].data); - PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name); - - /* Free the memory */ - kfree(stv680); -} - -static void stv680_disconnect (struct usb_interface *intf) -{ - struct usb_stv *stv680 = usb_get_intfdata (intf); - - usb_set_intfdata (intf, NULL); - - if (stv680) { - /* We don't want people trying to open up the device */ - if (stv680->vdev) { - stv680_remove_sysfs_files(stv680->vdev); - video_unregister_device(stv680->vdev); - stv680->vdev = NULL; - } - if (!stv680->user) { - usb_stv680_remove_disconnected (stv680); - } else { - stv680->removed = 1; - } - } -} - -static struct usb_driver stv680_driver = { - .name = "stv680", - .probe = stv680_probe, - .disconnect = stv680_disconnect, - .id_table = device_table -}; - -/******************************************************************** - * Module routines - ********************************************************************/ - -static int __init usb_stv680_init (void) -{ - if (usb_register (&stv680_driver) < 0) { - PDEBUG (0, "STV(e): Could not setup STV0680 driver"); - return -1; - } - PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION); - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - return 0; -} - -static void __exit usb_stv680_exit (void) -{ - usb_deregister (&stv680_driver); - PDEBUG (0, "STV(i): driver deregistered"); -} - -module_init (usb_stv680_init); -module_exit (usb_stv680_exit); diff --git a/drivers/media/video/stv680.h b/drivers/media/video/stv680.h deleted file mode 100644 index a08f1b08a4b..00000000000 --- a/drivers/media/video/stv680.h +++ /dev/null @@ -1,227 +0,0 @@ -/**************************************************************************** - * - * Filename: stv680.h - * - * Description: - * This is a USB driver for STV0680 based usb video cameras. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ****************************************************************************/ - -/* size of usb transfers */ -#define STV680_PACKETSIZE 4096 - -/* number of queued bulk transfers to use, may have problems if > 1 */ -#define STV680_NUMSBUF 1 - -/* number of frames supported by the v4l part */ -#define STV680_NUMFRAMES 2 - -/* scratch buffers for passing data to the decoders: 2 or 4 are good */ -#define STV680_NUMSCRATCH 2 - -/* number of nul sized packets to receive before kicking the camera */ -#define STV680_MAX_NULLPACKETS 200 - -/* number of decoding errors before kicking the camera */ -#define STV680_MAX_ERRORS 100 - -#define USB_PENCAM_VENDOR_ID 0x0553 -#define USB_PENCAM_PRODUCT_ID 0x0202 - -#define USB_CREATIVEGOMINI_VENDOR_ID 0x041e -#define USB_CREATIVEGOMINI_PRODUCT_ID 0x4007 - -#define PENCAM_TIMEOUT 1000 -/* fmt 4 */ -#define STV_VIDEO_PALETTE VIDEO_PALETTE_RGB24 - -static struct usb_device_id device_table[] = { - {USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)}, - {USB_DEVICE (USB_CREATIVEGOMINI_VENDOR_ID, USB_CREATIVEGOMINI_PRODUCT_ID)}, - {} -}; -MODULE_DEVICE_TABLE (usb, device_table); - -struct stv680_sbuf { - unsigned char *data; -}; - -enum { - FRAME_UNUSED, /* Unused (no MCAPTURE) */ - FRAME_READY, /* Ready to start grabbing */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_ERROR, /* Something bad happened while processing */ -}; - -enum { - BUFFER_UNUSED, - BUFFER_READY, - BUFFER_BUSY, - BUFFER_DONE, -}; - -/* raw camera data <- sbuf (urb transfer buf) */ -struct stv680_scratch { - unsigned char *data; - volatile int state; - int offset; - int length; -}; - -/* processed data for display ends up here, after bayer */ -struct stv680_frame { - unsigned char *data; /* Frame buffer */ - volatile int grabstate; /* State of grabbing */ - unsigned char *curline; - int curlinepix; - int curpix; -}; - -/* this is almost the video structure uvd_t, with extra parameters for stv */ -struct usb_stv { - struct video_device *vdev; - - struct usb_device *udev; - - unsigned char bulk_in_endpointAddr; /* __u8 the address of the bulk in endpoint */ - char *camera_name; - - unsigned int VideoMode; /* 0x0100 = VGA, 0x0000 = CIF, 0x0300 = QVGA */ - int SupportedModes; - int CIF; - int VGA; - int QVGA; - int cwidth; /* camera width */ - int cheight; /* camera height */ - int maxwidth; /* max video width */ - int maxheight; /* max video height */ - int vwidth; /* current width for video window */ - int vheight; /* current height for video window */ - unsigned long int rawbufsize; - unsigned long int maxframesize; /* rawbufsize * 3 for RGB */ - - int origGain; - int origMode; /* original camera mode */ - - struct mutex lock; /* to lock the structure */ - int user; /* user count for exclusive use */ - int removed; /* device disconnected */ - int streaming; /* Are we streaming video? */ - char *fbuf; /* Videodev buffer area */ - struct urb *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */ - int curframe; /* Current receiving frame */ - struct stv680_frame frame[STV680_NUMFRAMES]; /* # frames supported by v4l part */ - int readcount; - int framecount; - int error; - int dropped; - int scratch_next; - int scratch_use; - int scratch_overflow; - struct stv680_scratch scratch[STV680_NUMSCRATCH]; /* for decoders */ - struct stv680_sbuf sbuf[STV680_NUMSBUF]; - - unsigned int brightness; - unsigned int chgbright; - unsigned int whiteness; - unsigned int colour; - unsigned int contrast; - unsigned int hue; - unsigned int palette; - unsigned int depth; /* rgb24 in bits */ - - wait_queue_head_t wq; /* Processes waiting */ - - int nullpackets; -}; - - -static const unsigned char red[256] = { - 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42, - 44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69, - 71, 71, 73, 75, 77, 78, 80, 81, 82, 84, 85, 87, - 88, 89, 90, 91, 93, 94, 95, 97, 98, 98, 99, 101, - 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - 114, 115, 116, 116, 117, 118, 119, 120, 121, 122, 123, 124, - 125, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, - 134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, 143, - 143, 143, 144, 145, 146, 147, 147, 148, 149, 150, 150, 151, - 152, 152, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159, - 159, 160, 161, 161, 161, 162, 163, 163, 164, 165, 165, 166, - 167, 167, 168, 168, 169, 170, 170, 170, 171, 171, 172, 173, - 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 179, 179, - 180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186, - 187, 187, 188, 188, 188, 188, 189, 190, 190, 191, 191, 192, - 192, 193, 193, 194, 195, 195, 196, 196, 197, 197, 197, 197, - 198, 198, 199, 199, 200, 201, 201, 202, 202, 203, 203, 204, - 204, 205, 205, 206, 206, 206, 206, 207, 207, 208, 208, 209, - 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, - 215, 215, 215, 216, 216, 217, 217, 218, 218, 218, 219, 219, - 220, 220, 221, 221 -}; - -static const unsigned char green[256] = { - 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47, - 50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77, - 79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97, - 98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113, - 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, - 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, - 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, - 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, - 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168, - 169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, - 177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185, - 186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, - 193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200, - 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, - 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, - 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, - 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, - 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, - 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, - 239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244, - 245, 245, 246, 246 -}; - -static const unsigned char blue[256] = { - 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51, - 55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84, - 86, 88, 90, 92, 94, 95, 97, 100, 101, 103, 104, 106, - 107, 110, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, - 125, 126, 127, 128, 129, 132, 133, 134, 135, 136, 137, 138, - 139, 140, 141, 143, 144, 145, 146, 147, 148, 149, 150, 151, - 152, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163, - 165, 166, 166, 167, 168, 169, 170, 171, 171, 172, 173, 174, - 176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184, - 185, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, - 194, 195, 196, 196, 198, 199, 200, 200, 201, 202, 202, 203, - 204, 204, 205, 205, 206, 207, 207, 209, 210, 210, 211, 212, - 212, 213, 213, 214, 215, 215, 216, 217, 217, 218, 218, 220, - 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, - 228, 228, 229, 229, 231, 231, 232, 233, 233, 234, 234, 235, - 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 242, 242, - 243, 243, 244, 244, 245, 246, 246, 247, 247, 248, 248, 249, - 249, 250, 250, 251, 251, 253, 253, 254, 254, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255 -}; diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig index 2c29ec659b4..1686ebfa695 100644 --- a/drivers/media/video/tlg2300/Kconfig +++ b/drivers/media/video/tlg2300/Kconfig @@ -3,7 +3,7 @@ config VIDEO_TLG2300 depends on VIDEO_DEV && I2C && INPUT && SND && DVB_CORE select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEO_IR + depends on VIDEO_IR select VIDEOBUF_VMALLOC select SND_PCM select VIDEOBUF_DVB diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index 256cc558ba1..4555f4a5f4c 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -227,12 +227,11 @@ static int firmware_download(struct usb_device *udev) fwlength = fw->size; - fwbuf = kzalloc(fwlength, GFP_KERNEL); + fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL); if (!fwbuf) { ret = -ENOMEM; goto out; } - memcpy(fwbuf, fw->data, fwlength); max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize; log("\t\t download size : %d", (int)max_packet_size); diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 8085ac39244..48f5c76ab52 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -179,7 +179,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = { /* Register parameters for 480P */ static const struct i2c_reg_value tvp7002_parms_480P[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0a, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, @@ -223,7 +223,7 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = { /* Register parameters for 1080I60 */ static const struct i2c_reg_value tvp7002_parms_1080I60[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, @@ -245,7 +245,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = { /* Register parameters for 1080P60 */ static const struct i2c_reg_value tvp7002_parms_1080P60[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, @@ -289,7 +289,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = { /* Register parameters for 720P60 */ static const struct i2c_reg_value tvp7002_parms_720P60[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x02, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, @@ -311,7 +311,7 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = { /* Register parameters for 720P50 */ static const struct i2c_reg_value tvp7002_parms_720P50[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, - { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0c, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 445dc93413e..a727962781a 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -768,7 +768,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, mf->width = priv->scale->width; mf->height = priv->scale->height; - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; mf->colorspace = V4L2_COLORSPACE_JPEG; mf->field = V4L2_FIELD_INTERLACED_BT; @@ -797,7 +797,7 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, /* * check color format */ - if (mf->code != V4L2_MBUS_FMT_YUYV8_2X8_BE) + if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8) return -EINVAL; mf->colorspace = V4L2_COLORSPACE_JPEG; @@ -824,7 +824,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, return -EINVAL; } - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; mf->colorspace = V4L2_COLORSPACE_JPEG; /* @@ -909,7 +909,7 @@ static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index, if (index) return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV8_2X8_BE; + *code = V4L2_MBUS_FMT_UYVY8_2X8; return 0; } diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig index adb1c044ad7..d6e16959f78 100644 --- a/drivers/media/video/usbvideo/Kconfig +++ b/drivers/media/video/usbvideo/Kconfig @@ -37,17 +37,3 @@ config USB_KONICAWC To compile this driver as a module, choose M here: the module will be called konicawc. -config USB_QUICKCAM_MESSENGER - tristate "USB Logitech Quickcam Messenger (DEPRECATED)" - depends on VIDEO_V4L1 - select VIDEO_USBVIDEO - ---help--- - This driver is DEPRECATED please use the gspca stv06xx module - instead. - - Say Y or M here to enable support for the USB Logitech Quickcam - Messenger webcam. - - To compile this driver as a module, choose M here: the - module will be called quickcam_messenger. - diff --git a/drivers/media/video/usbvideo/Makefile b/drivers/media/video/usbvideo/Makefile index 4a1b144bee4..bb52eb8dc2f 100644 --- a/drivers/media/video/usbvideo/Makefile +++ b/drivers/media/video/usbvideo/Makefile @@ -2,4 +2,3 @@ obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o obj-$(CONFIG_USB_KONICAWC) += konicawc.o obj-$(CONFIG_USB_VICAM) += vicam.o -obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c deleted file mode 100644 index fbd665fa197..00000000000 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * Driver for Logitech Quickcam Messenger usb video camera - * Copyright (C) Jaya Kumar - * - * This work was sponsored by CIS(M) Sdn Bhd. - * History: - * 05/08/2006 - Jaya Kumar - * I wrote this based on the konicawc by Simon Evans. - * - - * Full credit for reverse engineering and creating an initial - * working linux driver for the VV6422 goes to the qce-ga project by - * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, - * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as - * others. - * --- - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/usb/input.h> -#include <linux/slab.h> - -#include "usbvideo.h" -#include "quickcam_messenger.h" - -/* - * Version Information - */ - -#ifdef CONFIG_USB_DEBUG -static int debug; -#define DEBUG(n, format, arg...) \ - if (n <= debug) { \ - printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \ - } -#else -#define DEBUG(n, arg...) -static const int debug; -#endif - -#define DRIVER_VERSION "v0.01" -#define DRIVER_DESC "Logitech Quickcam Messenger USB" - -#define USB_LOGITECH_VENDOR_ID 0x046D -#define USB_QCM_PRODUCT_ID 0x08F0 - -#define MAX_CAMERAS 1 - -#define MAX_COLOUR 32768 -#define MAX_HUE 32768 -#define MAX_BRIGHTNESS 32768 -#define MAX_CONTRAST 32768 -#define MAX_WHITENESS 32768 - -static int size = SIZE_320X240; -static int colour = MAX_COLOUR; -static int hue = MAX_HUE; -static int brightness = MAX_BRIGHTNESS; -static int contrast = MAX_CONTRAST; -static int whiteness = MAX_WHITENESS; - -static struct usbvideo *cams; - -static struct usb_device_id qcm_table [] = { - { USB_DEVICE(USB_LOGITECH_VENDOR_ID, USB_QCM_PRODUCT_ID) }, - { } -}; -MODULE_DEVICE_TABLE(usb, qcm_table); - -#ifdef CONFIG_INPUT -static void qcm_register_input(struct qcm *cam, struct usb_device *dev) -{ - struct input_dev *input_dev; - int error; - - usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); - - cam->input = input_dev = input_allocate_device(); - if (!input_dev) { - dev_warn(&dev->dev, "insufficient mem for cam input device\n"); - return; - } - - input_dev->name = "QCM button"; - input_dev->phys = cam->input_physname; - usb_to_input_id(dev, &input_dev->id); - input_dev->dev.parent = &dev->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY); - input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); - - error = input_register_device(cam->input); - if (error) { - dev_warn(&dev->dev, - "Failed to register camera's input device, err: %d\n", - error); - input_free_device(cam->input); - cam->input = NULL; - } -} - -static void qcm_unregister_input(struct qcm *cam) -{ - if (cam->input) { - input_unregister_device(cam->input); - cam->input = NULL; - } -} - -static void qcm_report_buttonstat(struct qcm *cam) -{ - if (cam->input) { - input_report_key(cam->input, KEY_CAMERA, cam->button_sts); - input_sync(cam->input); - } -} - -static void qcm_int_irq(struct urb *urb) -{ - int ret; - struct uvd *uvd = urb->context; - struct qcm *cam; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; - - if (!uvd->streaming) - return; - - uvd->stats.urb_count++; - - if (urb->status < 0) - uvd->stats.iso_err_count++; - else { - if (urb->actual_length > 0 ) { - cam = (struct qcm *) uvd->user_data; - if (cam->button_sts_buf == 0x88) - cam->button_sts = 0x0; - else if (cam->button_sts_buf == 0x80) - cam->button_sts = 0x1; - qcm_report_buttonstat(cam); - } - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) - err("usb_submit_urb error (%d)", ret); -} - -static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd) -{ - int errflag; - usb_fill_int_urb(cam->button_urb, uvd->dev, - usb_rcvintpipe(uvd->dev, uvd->video_endp + 1), - &cam->button_sts_buf, - 1, - qcm_int_irq, - uvd, 16); - - errflag = usb_submit_urb(cam->button_urb, GFP_KERNEL); - if (errflag) - err ("usb_submit_int ret %d", errflag); - return errflag; -} - -static void qcm_stop_int_data(struct qcm *cam) -{ - usb_kill_urb(cam->button_urb); -} - -static int qcm_alloc_int_urb(struct qcm *cam) -{ - cam->button_urb = usb_alloc_urb(0, GFP_KERNEL); - - if (!cam->button_urb) - return -ENOMEM; - - return 0; -} - -static void qcm_free_int(struct qcm *cam) -{ - usb_free_urb(cam->button_urb); -} -#endif /* CONFIG_INPUT */ - -static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val) -{ - int ret; - - /* we'll wait up to 3 slices but no more */ - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - reg, 0, &val, 1, 3*HZ); - return ret; -} - -static int qcm_stv_setw(struct usb_device *dev, u16 reg, __le16 val) -{ - int ret; - - /* we'll wait up to 3 slices but no more */ - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - reg, 0, &val, 2, 3*HZ); - return ret; -} - -static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, - __le16 *val) -{ - int ret; - - /* we'll wait up to 3 slices but no more */ - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x04, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, - reg, 0, val, 2, 3*HZ); - return ret; -} - -static int qcm_camera_on(struct uvd *uvd) -{ - int ret; - CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x01)); - return 0; -} - -static int qcm_camera_off(struct uvd *uvd) -{ - int ret; - CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); - return 0; -} - -static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) -{ - unsigned int segment, valsat; - signed int h = (signed int) hue; - unsigned int s = (sat - 32768) * 2; /* rescale */ - unsigned int v = val; - unsigned int p; - - /* - the registers controlling gain are 8 bit of which - we affect only the last 4 bits with our gain. - we know that if saturation is 0, (unsaturated) then - we're grayscale (center axis of the colour cone) so - we set rgb=value. we use a formula obtained from - wikipedia to map the cone to the RGB plane. it's - as follows for the human value case of h=0..360, - s=0..1, v=0..1 - h_i = h/60 % 6 , f = h/60 - h_i , p = v(1-s) - q = v(1 - f*s) , t = v(1 - (1-f)s) - h_i==0 => r=v , g=t, b=p - h_i==1 => r=q , g=v, b=p - h_i==2 => r=p , g=v, b=t - h_i==3 => r=p , g=q, b=v - h_i==4 => r=t , g=p, b=v - h_i==5 => r=v , g=p, b=q - the bottom side (the point) and the stuff just up - of that is black so we simplify those two cases. - */ - if (sat < 32768) { - /* anything less than this is unsaturated */ - *r = val; - *g = val; - *b = val; - return; - } - if (val <= (0xFFFF/8)) { - /* anything less than this is black */ - *r = 0; - *g = 0; - *b = 0; - return; - } - - /* the rest of this code is copying tukkat's - implementation of the hsv2rgb conversion as taken - from qc-usb-messenger code. the 10923 is 0xFFFF/6 - to divide the cone into 6 sectors. */ - - segment = (h + 10923) & 0xFFFF; - segment = segment*3 >> 16; /* 0..2: 0=R, 1=G, 2=B */ - hue -= segment * 21845; /* -10923..10923 */ - h = hue; - h *= 3; - valsat = v*s >> 16; /* 0..65534 */ - p = v - valsat; - if (h >= 0) { - unsigned int t = v - (valsat * (32769 - h) >> 15); - switch (segment) { - case 0: /* R-> */ - *r = v; - *g = t; - *b = p; - break; - case 1: /* G-> */ - *r = p; - *g = v; - *b = t; - break; - case 2: /* B-> */ - *r = t; - *g = p; - *b = v; - break; - } - } else { - unsigned int q = v - (valsat * (32769 + h) >> 15); - switch (segment) { - case 0: /* ->R */ - *r = v; - *g = p; - *b = q; - break; - case 1: /* ->G */ - *r = q; - *g = v; - *b = p; - break; - case 2: /* ->B */ - *r = p; - *g = q; - *b = v; - break; - } - } -} - -static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, - u16 saturation, u16 value) -{ - int ret; - u16 r=0,g=0,b=0; - - /* this code is based on qc-usb-messenger */ - qcm_hsv2rgb(hue, saturation, value, &r, &g, &b); - - r >>= 12; - g >>= 12; - b >>= 12; - - /* min val is 8 */ - r = max((u16) 8, r); - g = max((u16) 8, g); - b = max((u16) 8, b); - - r |= 0x30; - g |= 0x30; - b |= 0x30; - - /* set the r,g,b gain registers */ - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b)); - - /* doing as qc-usb did */ - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050C, 0x2A)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050D, 0x01)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); - - return 0; -} - -static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure) -{ - int ret; - int formedval; - - /* calculation was from qc-usb-messenger driver */ - formedval = ( exposure >> 12 ); - - /* max value for formedval is 14 */ - formedval = min(formedval, 14); - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, - 0x143A, 0xF0 | formedval)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); - return 0; -} - -static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast, - int hue, int colour) -{ - int ret; - /* brightness is exposure, contrast is gain, colour is saturation */ - CHECK_RET(ret, - qcm_sensor_set_exposure(uvd, brightness)); - CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast)); - - return 0; -} - -static int qcm_sensor_setsize(struct uvd *uvd, u8 size) -{ - int ret; - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x1505, size)); - return 0; -} - -static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness) -{ - int ret; - /* some rescaling as done by the qc-usb-messenger code */ - if (whiteness > 0xC000) - whiteness = 0xC000 + (whiteness & 0x3FFF)*8; - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, - (whiteness >> 8) & 0xFF)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, - (whiteness >> 16) & 0x03)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); - - return 0; -} - -static int qcm_sensor_init(struct uvd *uvd) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - int ret; - int i; - - for (i=0; i < ARRAY_SIZE(regval_table) ; i++) { - CHECK_RET(ret, qcm_stv_setb(uvd->dev, - regval_table[i].reg, - regval_table[i].val)); - } - - CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, - cpu_to_le16(ISOC_PACKET_SIZE))); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143f, 0x01)); - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); - - CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - - CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, - uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour)); - - CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); - CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - - return 0; -} - -static int qcm_set_camera_size(struct uvd *uvd) -{ - int ret; - struct qcm *cam = (struct qcm *) uvd->user_data; - - CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - cam->width = camera_sizes[cam->size].width; - cam->height = camera_sizes[cam->size].height; - uvd->videosize = VIDEOSIZE(cam->width, cam->height); - - return 0; -} - -static int qcm_setup_on_open(struct uvd *uvd) -{ - int ret; - - CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, - uvd->vpic.colour, uvd->vpic.contrast)); - CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness)); - CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); - CHECK_RET(ret, qcm_set_camera_size(uvd)); - CHECK_RET(ret, qcm_camera_on(uvd)); - return 0; -} - -static void qcm_adjust_picture(struct uvd *uvd) -{ - int ret; - struct qcm *cam = (struct qcm *) uvd->user_data; - - ret = qcm_camera_off(uvd); - if (ret) { - err("can't turn camera off. abandoning pic adjustment"); - return; - } - - /* if there's been a change in contrast, hue, or - colour then we need to recalculate hsv in order - to update gains */ - if ((cam->contrast != uvd->vpic.contrast) || - (cam->hue != uvd->vpic.hue) || - (cam->colour != uvd->vpic.colour)) { - cam->contrast = uvd->vpic.contrast; - cam->hue = uvd->vpic.hue; - cam->colour = uvd->vpic.colour; - ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, - cam->contrast); - if (ret) { - err("can't set gains. abandoning pic adjustment"); - return; - } - } - - if (cam->brightness != uvd->vpic.brightness) { - cam->brightness = uvd->vpic.brightness; - ret = qcm_sensor_set_exposure(uvd, cam->brightness); - if (ret) { - err("can't set exposure. abandoning pic adjustment"); - return; - } - } - - if (cam->whiteness != uvd->vpic.whiteness) { - cam->whiteness = uvd->vpic.whiteness; - qcm_sensor_set_shutter(uvd, cam->whiteness); - if (ret) { - err("can't set shutter. abandoning pic adjustment"); - return; - } - } - - ret = qcm_camera_on(uvd); - if (ret) { - err("can't reenable camera. pic adjustment failed"); - return; - } -} - -static int qcm_process_frame(struct uvd *uvd, u8 *cdata, int framelen) -{ - int datalen; - int totaldata; - struct framehdr { - __be16 id; - __be16 len; - }; - struct framehdr *fhdr; - - totaldata = 0; - while (framelen) { - fhdr = (struct framehdr *) cdata; - datalen = be16_to_cpu(fhdr->len); - framelen -= 4; - cdata += 4; - - if ((fhdr->id) == cpu_to_be16(0x8001)) { - RingQueue_Enqueue(&uvd->dp, marker, 4); - totaldata += 4; - continue; - } - if ((fhdr->id & cpu_to_be16(0xFF00)) == cpu_to_be16(0x0200)) { - RingQueue_Enqueue(&uvd->dp, cdata, datalen); - totaldata += datalen; - } - framelen -= datalen; - cdata += datalen; - } - return totaldata; -} - -static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb) -{ - int totlen; - int i; - unsigned char *cdata; - - totlen=0; - for (i = 0; i < dataurb->number_of_packets; i++) { - int n = dataurb->iso_frame_desc[i].actual_length; - int st = dataurb->iso_frame_desc[i].status; - - cdata = dataurb->transfer_buffer + - dataurb->iso_frame_desc[i].offset; - - if (st < 0) { - dev_warn(&uvd->dev->dev, - "Data error: packet=%d. len=%d. status=%d.\n", - i, n, st); - uvd->stats.iso_err_count++; - continue; - } - if (!n) - continue; - - totlen += qcm_process_frame(uvd, cdata, n); - } - return totlen; -} - -static void resubmit_urb(struct uvd *uvd, struct urb *urb) -{ - int ret; - - urb->dev = uvd->dev; - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - err("usb_submit_urb error (%d)", ret); -} - -static void qcm_isoc_irq(struct urb *urb) -{ - int len; - struct uvd *uvd = urb->context; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; - - if (!uvd->streaming) - return; - - uvd->stats.urb_count++; - - if (!urb->actual_length) { - resubmit_urb(uvd, urb); - return; - } - - len = qcm_compress_iso(uvd, urb); - resubmit_urb(uvd, urb); - uvd->stats.urb_length = len; - uvd->stats.data_count += len; - if (len) - RingQueue_WakeUpInterruptible(&uvd->dp); -} - -static int qcm_start_data(struct uvd *uvd) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - int i; - int errflag; - int pktsz; - int err; - - pktsz = uvd->iso_packet_len; - if (!CAMERA_IS_OPERATIONAL(uvd)) { - err("Camera is not operational"); - return -EFAULT; - } - - err = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltActive); - if (err < 0) { - err("usb_set_interface error"); - uvd->last_error = err; - return -EBUSY; - } - - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - int j, k; - struct urb *urb = uvd->sbuf[i].urb; - urb->dev = uvd->dev; - urb->context = uvd; - urb->pipe = usb_rcvisocpipe(uvd->dev, uvd->video_endp); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = uvd->sbuf[i].data; - urb->complete = qcm_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = pktsz; - } - } - - uvd->streaming = 1; - uvd->curframe = -1; - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - errflag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); - if (errflag) - err ("usb_submit_isoc(%d) ret %d", i, errflag); - } - - CHECK_RET(err, qcm_setup_input_int(cam, uvd)); - CHECK_RET(err, qcm_camera_on(uvd)); - return 0; -} - -static void qcm_stop_data(struct uvd *uvd) -{ - struct qcm *cam; - int i, j; - int ret; - - if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) - return; - cam = (struct qcm *) uvd->user_data; - - ret = qcm_camera_off(uvd); - if (ret) - dev_warn(&uvd->dev->dev, "couldn't turn the cam off.\n"); - - uvd->streaming = 0; - - /* Unschedule all of the iso td's */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) - usb_kill_urb(uvd->sbuf[i].urb); - - qcm_stop_int_data(cam); - - if (!uvd->remove_pending) { - /* Set packet size to 0 */ - j = usb_set_interface(uvd->dev, uvd->iface, - uvd->ifaceAltInactive); - if (j < 0) { - err("usb_set_interface() error %d.", j); - uvd->last_error = j; - } - } -} - -static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - int x; - struct rgb *rgbL0; - struct rgb *rgbL1; - struct bayL0 *bayL0; - struct bayL1 *bayL1; - int hor,ver,hordel,verdel; - assert(frame != NULL); - - switch (cam->size) { - case SIZE_160X120: - hor = 162; ver = 124; hordel = 1; verdel = 2; - break; - case SIZE_320X240: - default: - hor = 324; ver = 248; hordel = 2; verdel = 4; - break; - } - - if (frame->scanstate == ScanState_Scanning) { - while (RingQueue_GetLength(&uvd->dp) >= - 4 + (hor*verdel + hordel)) { - if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && - (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && - (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && - (RING_QUEUE_PEEK(&uvd->dp, 3) == 0xff)) { - frame->curline = 0; - frame->scanstate = ScanState_Lines; - frame->frameState = FrameState_Grabbing; - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); - /* - * if we're starting, we need to discard the first - * 4 lines of y bayer data - * and the first 2 gr elements of x bayer data - */ - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, - (hor*verdel + hordel)); - break; - } - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); - } - } - - if (frame->scanstate == ScanState_Scanning) - return; - - /* now we can start processing bayer data so long as we have at least - * 2 lines worth of data. this is the simplest demosaicing method that - * I could think of. I use each 2x2 bayer element without interpolation - * to generate 4 rgb pixels. - */ - while ( frame->curline < cam->height && - (RingQueue_GetLength(&uvd->dp) >= hor*2)) { - /* get 2 lines of bayer for demosaicing - * into 2 lines of RGB */ - RingQueue_Dequeue(&uvd->dp, cam->scratch, hor*2); - bayL0 = (struct bayL0 *) cam->scratch; - bayL1 = (struct bayL1 *) (cam->scratch + hor); - /* frame->curline is the rgb y line */ - rgbL0 = (struct rgb *) - ( frame->data + (cam->width*3*frame->curline)); - /* w/2 because we're already doing 2 pixels */ - rgbL1 = rgbL0 + (cam->width/2); - - for (x=0; x < cam->width; x+=2) { - rgbL0->r = bayL0->r; - rgbL0->g = bayL0->g; - rgbL0->b = bayL1->b; - - rgbL0->r2 = bayL0->r; - rgbL0->g2 = bayL1->g; - rgbL0->b2 = bayL1->b; - - rgbL1->r = bayL0->r; - rgbL1->g = bayL1->g; - rgbL1->b = bayL1->b; - - rgbL1->r2 = bayL0->r; - rgbL1->g2 = bayL1->g; - rgbL1->b2 = bayL1->b; - - rgbL0++; - rgbL1++; - - bayL0++; - bayL1++; - } - - frame->seqRead_Length += cam->width*3*2; - frame->curline += 2; - } - /* See if we filled the frame */ - if (frame->curline == cam->height) { - frame->frameState = FrameState_Done_Hold; - frame->curline = 0; - uvd->curframe = -1; - uvd->stats.frame_num++; - } -} - -/* taken from konicawc */ -static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw) -{ - int ret; - int newsize; - int oldsize; - int x = vw->width; - int y = vw->height; - struct qcm *cam = (struct qcm *) uvd->user_data; - - if (x > 0 && y > 0) { - DEBUG(2, "trying to find size %d,%d", x, y); - for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { - if ((camera_sizes[newsize].width == x) && - (camera_sizes[newsize].height == y)) - break; - } - } else - newsize = cam->size; - - if (newsize > MAX_FRAME_SIZE) { - DEBUG(1, "couldn't find size %d,%d", x, y); - return -EINVAL; - } - - if (newsize == cam->size) { - DEBUG(1, "Nothing to do"); - return 0; - } - - qcm_stop_data(uvd); - - if (cam->size != newsize) { - oldsize = cam->size; - cam->size = newsize; - ret = qcm_set_camera_size(uvd); - if (ret) { - err("Couldn't set camera size, err=%d",ret); - /* restore the original size */ - cam->size = oldsize; - return ret; - } - } - - /* Flush the input queue and clear any current frame in progress */ - - RingQueue_Flush(&uvd->dp); - if (uvd->curframe != -1) { - uvd->frame[uvd->curframe].curline = 0; - uvd->frame[uvd->curframe].seqRead_Length = 0; - uvd->frame[uvd->curframe].seqRead_Index = 0; - } - - CHECK_RET(ret, qcm_start_data(uvd)); - return 0; -} - -static int qcm_configure_video(struct uvd *uvd) -{ - int ret; - memset(&uvd->vpic, 0, sizeof(uvd->vpic)); - memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); - - uvd->vpic.colour = colour; - uvd->vpic.hue = hue; - uvd->vpic.brightness = brightness; - uvd->vpic.contrast = contrast; - uvd->vpic.whiteness = whiteness; - uvd->vpic.depth = 24; - uvd->vpic.palette = VIDEO_PALETTE_RGB24; - - memset(&uvd->vcap, 0, sizeof(uvd->vcap)); - strcpy(uvd->vcap.name, "QCM USB Camera"); - uvd->vcap.type = VID_TYPE_CAPTURE; - uvd->vcap.channels = 1; - uvd->vcap.audios = 0; - - uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width; - uvd->vcap.minheight = camera_sizes[SIZE_160X120].height; - uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width; - uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height; - - memset(&uvd->vchan, 0, sizeof(uvd->vchan)); - uvd->vchan.flags = 0 ; - uvd->vchan.tuners = 0; - uvd->vchan.channel = 0; - uvd->vchan.type = VIDEO_TYPE_CAMERA; - strcpy(uvd->vchan.name, "Camera"); - - CHECK_RET(ret, qcm_sensor_init(uvd)); - return 0; -} - -static int qcm_probe(struct usb_interface *intf, - const struct usb_device_id *devid) -{ - int err; - struct uvd *uvd; - struct usb_device *dev = interface_to_usbdev(intf); - struct qcm *cam; - size_t buffer_size; - unsigned char video_ep; - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - int i,j; - unsigned int ifacenum, ifacenum_inact=0; - __le16 sensor_id; - - /* we don't support multiconfig cams */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - /* first check for the video interface and not - * the audio interface */ - interface = &intf->cur_altsetting[0]; - if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) - || (interface->desc.bInterfaceSubClass != - USB_CLASS_VENDOR_SPEC)) - return -ENODEV; - - /* - walk through each endpoint in each setting in the interface - stop when we find the one that's an isochronous IN endpoint. - */ - for (i=0; i < intf->num_altsetting; i++) { - interface = &intf->cur_altsetting[i]; - ifacenum = interface->desc.bAlternateSetting; - /* walk the end points */ - for (j=0; j < interface->desc.bNumEndpoints; j++) { - endpoint = &interface->endpoint[j].desc; - - if (usb_endpoint_dir_out(endpoint)) - continue; /* not input then not good */ - - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); - if (!buffer_size) { - ifacenum_inact = ifacenum; - continue; /* 0 pkt size is not what we want */ - } - - if (usb_endpoint_xfer_isoc(endpoint)) { - video_ep = endpoint->bEndpointAddress; - /* break out of the search */ - goto good_videoep; - } - } - } - /* failed out since nothing useful was found */ - err("No suitable endpoint was found\n"); - return -ENODEV; - -good_videoep: - /* disable isochronous stream before doing anything else */ - err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); - if (err < 0) { - err("Failed to disable sensor stream"); - return -EIO; - } - - /* - Check that this is the same unknown sensor that is known to work. This - sensor is suspected to be the ST VV6422C001. I'll check the same value - that the qc-usb driver checks. This value is probably not even the - sensor ID since it matches the USB dev ID. Oh well. If it doesn't - match, it's probably a diff sensor so exit and apologize. - */ - err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id); - if (err < 0) { - err("Couldn't read sensor values. Err %d\n",err); - return err; - } - if (sensor_id != cpu_to_le16(0x08F0)) { - err("Sensor ID %x != %x. Unsupported. Sorry\n", - le16_to_cpu(sensor_id), (0x08F0)); - return -ENODEV; - } - - uvd = usbvideo_AllocateDevice(cams); - if (!uvd) - return -ENOMEM; - - cam = (struct qcm *) uvd->user_data; - - /* buf for doing demosaicing */ - cam->scratch = kmalloc(324*2, GFP_KERNEL); - if (!cam->scratch) /* uvd freed in dereg */ - return -ENOMEM; - - /* yes, if we fail after here, cam->scratch gets freed - by qcm_free_uvd */ - - err = qcm_alloc_int_urb(cam); - if (err < 0) - return err; - - /* yes, if we fail after here, int urb gets freed - by qcm_free_uvd */ - - RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); - cam->width = camera_sizes[size].width; - cam->height = camera_sizes[size].height; - cam->size = size; - - uvd->debug = debug; - uvd->flags = 0; - uvd->dev = dev; - uvd->iface = intf->altsetting->desc.bInterfaceNumber; - uvd->ifaceAltActive = ifacenum; - uvd->ifaceAltInactive = ifacenum_inact; - uvd->video_endp = video_ep; - uvd->iso_packet_len = buffer_size; - uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; - uvd->defaultPalette = VIDEO_PALETTE_RGB24; - uvd->canvas = VIDEOSIZE(320, 240); - uvd->videosize = VIDEOSIZE(cam->width, cam->height); - err = qcm_configure_video(uvd); - if (err) { - err("failed to configure video settings"); - return err; - } - - err = usbvideo_RegisterVideoDevice(uvd); - if (err) { /* the uvd gets freed in Deregister */ - err("usbvideo_RegisterVideoDevice() failed."); - return err; - } - - uvd->max_frame_size = (320 * 240 * 3); - qcm_register_input(cam, dev); - usb_set_intfdata(intf, uvd); - return 0; -} - -static void qcm_free_uvd(struct uvd *uvd) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - - kfree(cam->scratch); - qcm_unregister_input(cam); - qcm_free_int(cam); -} - -static struct usbvideo_cb qcm_driver = { - .probe = qcm_probe, - .setupOnOpen = qcm_setup_on_open, - .processData = qcm_process_isoc, - .setVideoMode = qcm_set_video_mode, - .startDataPump = qcm_start_data, - .stopDataPump = qcm_stop_data, - .adjustPicture = qcm_adjust_picture, - .userFree = qcm_free_uvd -}; - -static int __init qcm_init(void) -{ - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - - return usbvideo_register( - &cams, - MAX_CAMERAS, - sizeof(struct qcm), - "QCM", - &qcm_driver, - THIS_MODULE, - qcm_table); -} - -static void __exit qcm_exit(void) -{ - usbvideo_Deregister(&cams); -} - -module_param(size, int, 0); -MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 320x240"); -module_param(colour, int, 0); -MODULE_PARM_DESC(colour, "Initial colour"); -module_param(hue, int, 0); -MODULE_PARM_DESC(hue, "Initial hue"); -module_param(brightness, int, 0); -MODULE_PARM_DESC(brightness, "Initial brightness"); -module_param(contrast, int, 0); -MODULE_PARM_DESC(contrast, "Initial contrast"); -module_param(whiteness, int, 0); -MODULE_PARM_DESC(whiteness, "Initial whiteness"); - -#ifdef CONFIG_USB_DEBUG -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); -#endif - -module_init(qcm_init); -module_exit(qcm_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jaya Kumar"); -MODULE_DESCRIPTION("QCM USB Camera"); -MODULE_SUPPORTED_DEVICE("QCM USB Camera"); diff --git a/drivers/media/video/usbvideo/quickcam_messenger.h b/drivers/media/video/usbvideo/quickcam_messenger.h deleted file mode 100644 index 17ace394d98..00000000000 --- a/drivers/media/video/usbvideo/quickcam_messenger.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef quickcam_messenger_h -#define quickcam_messenger_h - -#ifndef CONFIG_INPUT -/* if we're not using input we dummy out these functions */ -#define qcm_register_input(...) -#define qcm_unregister_input(...) -#define qcm_report_buttonstat(...) -#define qcm_setup_input_int(...) 0 -#define qcm_stop_int_data(...) -#define qcm_alloc_int_urb(...) 0 -#define qcm_free_int(...) -#endif - - -#define CHECK_RET(ret, expr) \ - if ((ret = expr) < 0) return ret - -/* Control Registers for the STVV6422 ASIC - * - this define is taken from the qc-usb-messenger code - */ -#define STV_ISO_ENABLE 0x1440 -#define ISOC_PACKET_SIZE 1023 - -/* Chip identification number including revision indicator */ -#define CMOS_SENSOR_IDREV 0xE00A - -struct rgb { - u8 b; - u8 g; - u8 r; - u8 b2; - u8 g2; - u8 r2; -}; - -struct bayL0 { - u8 g; - u8 r; -}; - -struct bayL1 { - u8 b; - u8 g; -}; - -struct cam_size { - u16 width; - u16 height; - u8 cmd; -}; - -static const struct cam_size camera_sizes[] = { - { 160, 120, 0xf }, - { 320, 240, 0x2 }, -}; - -enum frame_sizes { - SIZE_160X120 = 0, - SIZE_320X240 = 1, -}; - -#define MAX_FRAME_SIZE SIZE_320X240 - -struct qcm { - u16 colour; - u16 hue; - u16 brightness; - u16 contrast; - u16 whiteness; - - u8 size; - int height; - int width; - u8 *scratch; - struct urb *button_urb; - u8 button_sts; - u8 button_sts_buf; - -#ifdef CONFIG_INPUT - struct input_dev *input; - char input_physname[64]; -#endif -}; - -struct regval { - u16 reg; - u8 val; -}; -/* this table is derived from the -qc-usb-messenger code */ -static const struct regval regval_table[] = { - { STV_ISO_ENABLE, 0x00 }, - { 0x1436, 0x00 }, { 0x1432, 0x03 }, - { 0x143a, 0xF9 }, { 0x0509, 0x38 }, - { 0x050a, 0x38 }, { 0x050b, 0x38 }, - { 0x050c, 0x2A }, { 0x050d, 0x01 }, - { 0x1431, 0x00 }, { 0x1433, 0x34 }, - { 0x1438, 0x18 }, { 0x1439, 0x00 }, - { 0x143b, 0x05 }, { 0x143c, 0x00 }, - { 0x143e, 0x01 }, { 0x143d, 0x00 }, - { 0x1442, 0xe2 }, { 0x1500, 0xd0 }, - { 0x1500, 0xd0 }, { 0x1500, 0x50 }, - { 0x1501, 0xaf }, { 0x1502, 0xc2 }, - { 0x1503, 0x45 }, { 0x1505, 0x02 }, - { 0x150e, 0x8e }, { 0x150f, 0x37 }, - { 0x15c0, 0x00 }, -}; - -static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF }; - -#endif /* quickcam_messenger_h */ diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index 5ac37c6c431..f1fcf974496 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -282,19 +282,15 @@ static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, }; unsigned short digit; int ix, iy; + int value; if ((uvd == NULL) || (frame == NULL)) return; - if (ch >= '0' && ch <= '9') - ch -= '0'; - else if (ch >= 'A' && ch <= 'F') - ch = 10 + (ch - 'A'); - else if (ch >= 'a' && ch <= 'f') - ch = 10 + (ch - 'a'); - else + value = hex_to_bin(ch); + if (value < 0) return; - digit = digits[ch]; + digit = digits[value]; for (iy=0; iy < 5; iy++) { for (ix=0; ix < 3; ix++) { diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 6030410c667..5d6fd01f918 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -2,7 +2,7 @@ * USB ViCam WebCam driver * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), * Christopher L Cheney (ccheney@cheney.cx), - * Pavel Machek (pavel@suse.cz), + * Pavel Machek (pavel@ucw.cz), * John Tyner (jtyner@cs.ucr.edu), * Monroe Williams (monroe@pobox.com) * diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 27a79f087b1..a350fad0db4 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -606,6 +606,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .set = uvc_ctrl_set_zoom, }, { + .id = V4L2_CID_PAN_ABSOLUTE, + .name = "Pan (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_ABSOLUTE, + .name = "Tilt (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 32, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, @@ -623,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) { - return ctrl->data + id * ctrl->info->size; + return ctrl->uvc_data + id * ctrl->info->size; } static inline int uvc_test_bit(const __u8 *data, int bit) @@ -678,6 +698,14 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, int offset = mapping->offset; __u8 mask; + /* According to the v4l2 spec, writing any value to a button control + * should result in the action belonging to the button control being + * triggered. UVC devices however want to see a 1 written -> override + * value. + */ + if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) + value = -1; + data += offset / 8; offset &= 7; @@ -1265,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) * Control and mapping handling */ -static void uvc_ctrl_add_ctrl(struct uvc_device *dev, +static int uvc_ctrl_add_ctrl(struct uvc_device *dev, struct uvc_control_info *info) { struct uvc_entity *entity; struct uvc_control *ctrl = NULL; - int ret, found = 0; + int ret = 0, found = 0; unsigned int i; + u8 *uvc_info; + u8 *uvc_data; list_for_each_entry(entity, &dev->entities, list) { if (!uvc_entity_match_guid(entity, info->entity)) @@ -1290,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, } if (!found) - return; + return 0; + + uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); + if (uvc_data == NULL) + return -ENOMEM; + + uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST; if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { /* Check if the device control information and length match * the user supplied information. */ - __u32 flags; - __le16 size; - __u8 inf; - ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, - dev->intfnum, info->selector, (__u8 *)&size, 2); + dev->intfnum, info->selector, uvc_data, 2); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on control %pUl/%u (%d).\n", info->entity, info->selector, ret); - return; + goto done; } - if (info->size != le16_to_cpu(size)) { + if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " "doesn't match user supplied value.\n", info->entity, info->selector); - return; + ret = -EINVAL; + goto done; } ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, - dev->intfnum, info->selector, &inf, 1); + dev->intfnum, info->selector, uvc_info, 1); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on control %pUl/%u (%d).\n", info->entity, info->selector, ret); - return; + goto done; } - flags = info->flags; - if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || - ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + if (((info->flags & UVC_CONTROL_GET_CUR) && + !(*uvc_info & UVC_CONTROL_CAP_GET)) || + ((info->flags & UVC_CONTROL_SET_CUR) && + !(*uvc_info & UVC_CONTROL_CAP_SET))) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " "don't match supported operations.\n", info->entity, info->selector); - return; + ret = -EINVAL; + goto done; } } ctrl->info = info; - ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL); + ctrl->uvc_data = uvc_data; + ctrl->uvc_info = uvc_info; + uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " "entity %u\n", ctrl->info->entity, ctrl->info->selector, dev->udev->devpath, entity->id); + +done: + if (ret < 0) + kfree(uvc_data); + + return ret; } /* @@ -1572,12 +1615,34 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) - kfree(entity->controls[i].data); + kfree(entity->controls[i].uvc_data); kfree(entity->controls); } } +void uvc_ctrl_cleanup(void) +{ + struct uvc_control_info *info; + struct uvc_control_info *ni; + struct uvc_control_mapping *mapping; + struct uvc_control_mapping *nm; + + list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) { + if (!(info->flags & UVC_CONTROL_EXTENSION)) + continue; + + list_for_each_entry_safe(mapping, nm, &info->mappings, list) { + list_del(&mapping->list); + kfree(mapping->menu_info); + kfree(mapping); + } + + list_del(&info->list); + kfree(info); + } +} + void uvc_ctrl_init(void) { struct uvc_control_info *ctrl = uvc_ctrls; diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 838b56f097c..8bdd940f32e 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -637,14 +637,13 @@ static int uvc_parse_streaming(struct uvc_device *dev, } streaming->header.bControlSize = n; - streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + streaming->header.bmaControls = kmemdup(&buffer[size], p * n, + GFP_KERNEL); if (streaming->header.bmaControls == NULL) { ret = -ENOMEM; goto error; } - memcpy(streaming->header.bmaControls, &buffer[size], p*n); - buflen -= buffer[0]; buffer += buffer[0]; @@ -2146,6 +2145,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Miricle 307K */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17dc, + .idProduct = 0x0202, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Lenovo Thinkpad SL400/SL500 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2174,6 +2182,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Manta MM-353 Plako */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3188, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* FSC WebCam V30S */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2261,6 +2278,7 @@ static int __init uvc_init(void) static void __exit uvc_cleanup(void) { usb_deregister(&uvc_driver.driver); + uvc_ctrl_cleanup(); } module_init(uvc_init); diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 133c78d113a..e9928a41508 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -78,12 +78,14 @@ * */ -void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) +void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, + int drop_corrupted) { mutex_init(&queue->mutex); spin_lock_init(&queue->irqlock); INIT_LIST_HEAD(&queue->mainqueue); INIT_LIST_HEAD(&queue->irqqueue); + queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0; queue->type = type; } @@ -435,8 +437,10 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable) uvc_queue_cancel(queue, 0); INIT_LIST_HEAD(&queue->mainqueue); - for (i = 0; i < queue->count; ++i) + for (i = 0; i < queue->count; ++i) { + queue->buffer[i].error = 0; queue->buffer[i].state = UVC_BUF_STATE_IDLE; + } queue->flags &= ~UVC_QUEUE_STREAMING; } @@ -488,8 +492,8 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *nextbuf; unsigned long flags; - if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && - buf->buf.length != buf->buf.bytesused) { + if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) { + buf->error = 0; buf->state = UVC_BUF_STATE_QUEUED; buf->buf.bytesused = 0; return buf; @@ -497,6 +501,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, spin_lock_irqsave(&queue->irqlock, flags); list_del(&buf->queue); + buf->error = 0; buf->state = UVC_BUF_STATE_DONE; if (!list_empty(&queue->irqqueue)) nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 7c9ab293349..86db32697b8 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -29,6 +29,71 @@ #include "uvcvideo.h" /* ------------------------------------------------------------------------ + * UVC ioctls + */ +static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) +{ + struct uvc_control_mapping *map; + unsigned int size; + int ret; + + map = kzalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + switch (xmap->v4l2_type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + break; + + case V4L2_CTRL_TYPE_MENU: + if (old) { + ret = -EINVAL; + goto done; + } + + size = xmap->menu_count * sizeof(*map->menu_info); + map->menu_info = kmalloc(size, GFP_KERNEL); + if (map->menu_info == NULL) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user(map->menu_info, xmap->menu_info, size)) { + ret = -EFAULT; + goto done; + } + + map->menu_count = xmap->menu_count; + break; + + default: + ret = -EINVAL; + goto done; + } + + ret = uvc_ctrl_add_mapping(map); + +done: + if (ret < 0) { + kfree(map->menu_info); + kfree(map); + } + + return ret; +} + +/* ------------------------------------------------------------------------ * V4L2 interface */ @@ -451,7 +516,7 @@ static int uvc_v4l2_open(struct file *file) static int uvc_v4l2_release(struct file *file) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); @@ -482,7 +547,7 @@ static int uvc_v4l2_release(struct file *file) static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_video_chain *chain = handle->chain; struct uvc_streaming *stream = handle->stream; long ret = 0; @@ -963,6 +1028,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (xinfo->size == 0) + return -EINVAL; + info = kzalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -974,7 +1042,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) info->flags = xinfo->flags; info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | - UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; + UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF | + UVC_CONTROL_EXTENSION; ret = uvc_ctrl_add_info(info); if (ret < 0) @@ -982,32 +1051,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } + case UVCIOC_CTRL_MAP_OLD: case UVCIOC_CTRL_MAP: - { - struct uvc_xu_control_mapping *xmap = arg; - struct uvc_control_mapping *map; - if (!capable(CAP_SYS_ADMIN)) return -EPERM; - map = kzalloc(sizeof *map, GFP_KERNEL); - if (map == NULL) - return -ENOMEM; - - map->id = xmap->id; - memcpy(map->name, xmap->name, sizeof map->name); - memcpy(map->entity, xmap->entity, sizeof map->entity); - map->selector = xmap->selector; - map->size = xmap->size; - map->offset = xmap->offset; - map->v4l2_type = xmap->v4l2_type; - map->data_type = xmap->data_type; - - ret = uvc_ctrl_add_mapping(map); - if (ret < 0) - kfree(map); - break; - } + return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD); case UVCIOC_CTRL_GET: return uvc_xu_ctrl_query(chain, arg, 0); @@ -1067,7 +1116,7 @@ static const struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *uninitialized_var(buffer); @@ -1122,7 +1171,7 @@ done: static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 53f3ef4635e..e27cf0d3b6d 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -555,6 +555,9 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, if (urb->iso_frame_desc[i].status < 0) { uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " "lost (%d).\n", urb->iso_frame_desc[i].status); + /* Mark the buffer as faulty. */ + if (buf != NULL) + buf->error = 1; continue; } @@ -579,8 +582,14 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (buf->state == UVC_BUF_STATE_READY) + if (buf->state == UVC_BUF_STATE_READY) { + if (buf->buf.length != buf->buf.bytesused && + !(stream->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + buf->error = 1; + buf = uvc_queue_next_buffer(&stream->queue, buf); + } } } @@ -1104,7 +1113,7 @@ int uvc_video_init(struct uvc_streaming *stream) atomic_set(&stream->active, 0); /* Initialize the video buffers queue. */ - uvc_queue_init(&stream->queue, stream->type); + uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if @@ -1197,12 +1206,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) return 0; } - if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || - uvc_no_drop_param) - stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; - else - stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; - ret = uvc_queue_enable(&stream->queue, 1); if (ret < 0) return ret; diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index d1f88406a5e..bdacf3beabf 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -27,6 +27,8 @@ #define UVC_CONTROL_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CONTROL_AUTO_UPDATE (1 << 7) +/* Control is an extension unit control. */ +#define UVC_CONTROL_EXTENSION (1 << 8) #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ @@ -40,6 +42,15 @@ struct uvc_xu_control_info { __u32 flags; }; +struct uvc_menu_info { + __u32 value; + __u8 name[32]; +}; + +struct uvc_xu_control_mapping_old { + __u8 reserved[64]; +}; + struct uvc_xu_control_mapping { __u32 id; __u8 name[32]; @@ -50,6 +61,11 @@ struct uvc_xu_control_mapping { __u8 offset; enum v4l2_ctrl_type v4l2_type; __u32 data_type; + + struct uvc_menu_info __user *menu_info; + __u32 menu_count; + + __u32 reserved[4]; }; struct uvc_xu_control { @@ -60,6 +76,7 @@ struct uvc_xu_control { }; #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) @@ -179,30 +196,6 @@ struct uvc_device; /* TODO: Put the most frequently accessed fields at the beginning of * structures to maximize cache efficiency. */ -struct uvc_streaming_control { - __u16 bmHint; - __u8 bFormatIndex; - __u8 bFrameIndex; - __u32 dwFrameInterval; - __u16 wKeyFrameRate; - __u16 wPFrameRate; - __u16 wCompQuality; - __u16 wCompWindowSize; - __u16 wDelay; - __u32 dwMaxVideoFrameSize; - __u32 dwMaxPayloadTransferSize; - __u32 dwClockFrequency; - __u8 bmFramingInfo; - __u8 bPreferedVersion; - __u8 bMinVersion; - __u8 bMaxVersion; -}; - -struct uvc_menu_info { - __u32 value; - __u8 name[32]; -}; - struct uvc_control_info { struct list_head list; struct list_head mappings; @@ -250,7 +243,8 @@ struct uvc_control { modified : 1, cached : 1; - __u8 *data; + __u8 *uvc_data; + __u8 *uvc_info; }; struct uvc_format_desc { @@ -385,11 +379,12 @@ struct uvc_buffer { struct list_head queue; wait_queue_head_t wait; enum uvc_buffer_state state; + unsigned int error; }; #define UVC_QUEUE_STREAMING (1 << 0) #define UVC_QUEUE_DISCONNECTED (1 << 1) -#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) +#define UVC_QUEUE_DROP_CORRUPTED (1 << 2) struct uvc_video_queue { enum v4l2_buf_type type; @@ -568,7 +563,7 @@ extern struct uvc_driver uvc_driver; /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type); + enum v4l2_buf_type type, int drop_corrupted); extern int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, unsigned int buflength); extern int uvc_free_buffers(struct uvc_video_queue *queue); @@ -625,6 +620,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev); extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern void uvc_ctrl_init(void); +extern void uvc_ctrl_cleanup(void); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 4e53b0b3339..8ee1179be92 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -62,6 +62,7 @@ #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include <media/v4l2-common.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> #include <linux/videodev2.h> @@ -172,487 +173,17 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, } EXPORT_SYMBOL(v4l2_ctrl_check); -/* Returns NULL or a character pointer array containing the menu for - the given control ID. The pointer array ends with a NULL pointer. - An empty string signifies a menu entry that is invalid. This allows - drivers to disable certain options if it is not supported. */ -const char **v4l2_ctrl_get_menu(u32 id) -{ - static const char *mpeg_audio_sampling_freq[] = { - "44.1 kHz", - "48 kHz", - "32 kHz", - NULL - }; - static const char *mpeg_audio_encoding[] = { - "MPEG-1/2 Layer I", - "MPEG-1/2 Layer II", - "MPEG-1/2 Layer III", - "MPEG-2/4 AAC", - "AC-3", - NULL - }; - static const char *mpeg_audio_l1_bitrate[] = { - "32 kbps", - "64 kbps", - "96 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "288 kbps", - "320 kbps", - "352 kbps", - "384 kbps", - "416 kbps", - "448 kbps", - NULL - }; - static const char *mpeg_audio_l2_bitrate[] = { - "32 kbps", - "48 kbps", - "56 kbps", - "64 kbps", - "80 kbps", - "96 kbps", - "112 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "320 kbps", - "384 kbps", - NULL - }; - static const char *mpeg_audio_l3_bitrate[] = { - "32 kbps", - "40 kbps", - "48 kbps", - "56 kbps", - "64 kbps", - "80 kbps", - "96 kbps", - "112 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "320 kbps", - NULL - }; - static const char *mpeg_audio_ac3_bitrate[] = { - "32 kbps", - "40 kbps", - "48 kbps", - "56 kbps", - "64 kbps", - "80 kbps", - "96 kbps", - "112 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "320 kbps", - "384 kbps", - "448 kbps", - "512 kbps", - "576 kbps", - "640 kbps", - NULL - }; - static const char *mpeg_audio_mode[] = { - "Stereo", - "Joint Stereo", - "Dual", - "Mono", - NULL - }; - static const char *mpeg_audio_mode_extension[] = { - "Bound 4", - "Bound 8", - "Bound 12", - "Bound 16", - NULL - }; - static const char *mpeg_audio_emphasis[] = { - "No Emphasis", - "50/15 us", - "CCITT J17", - NULL - }; - static const char *mpeg_audio_crc[] = { - "No CRC", - "16-bit CRC", - NULL - }; - static const char *mpeg_video_encoding[] = { - "MPEG-1", - "MPEG-2", - "MPEG-4 AVC", - NULL - }; - static const char *mpeg_video_aspect[] = { - "1x1", - "4x3", - "16x9", - "2.21x1", - NULL - }; - static const char *mpeg_video_bitrate_mode[] = { - "Variable Bitrate", - "Constant Bitrate", - NULL - }; - static const char *mpeg_stream_type[] = { - "MPEG-2 Program Stream", - "MPEG-2 Transport Stream", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - static const char *mpeg_stream_vbi_fmt[] = { - "No VBI", - "Private packet, IVTV format", - NULL - }; - static const char *camera_power_line_frequency[] = { - "Disabled", - "50 Hz", - "60 Hz", - NULL - }; - static const char *camera_exposure_auto[] = { - "Auto Mode", - "Manual Mode", - "Shutter Priority Mode", - "Aperture Priority Mode", - NULL - }; - static const char *colorfx[] = { - "None", - "Black & White", - "Sepia", - "Negative", - "Emboss", - "Sketch", - "Sky blue", - "Grass green", - "Skin whiten", - "Vivid", - NULL - }; - static const char *tune_preemphasis[] = { - "No preemphasis", - "50 useconds", - "75 useconds", - NULL, - }; - - switch (id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return mpeg_audio_sampling_freq; - case V4L2_CID_MPEG_AUDIO_ENCODING: - return mpeg_audio_encoding; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - return mpeg_audio_l1_bitrate; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - return mpeg_audio_l2_bitrate; - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return mpeg_audio_l3_bitrate; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - return mpeg_audio_ac3_bitrate; - case V4L2_CID_MPEG_AUDIO_MODE: - return mpeg_audio_mode; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - return mpeg_audio_mode_extension; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return mpeg_audio_emphasis; - case V4L2_CID_MPEG_AUDIO_CRC: - return mpeg_audio_crc; - case V4L2_CID_MPEG_VIDEO_ENCODING: - return mpeg_video_encoding; - case V4L2_CID_MPEG_VIDEO_ASPECT: - return mpeg_video_aspect; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return mpeg_video_bitrate_mode; - case V4L2_CID_MPEG_STREAM_TYPE: - return mpeg_stream_type; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - return mpeg_stream_vbi_fmt; - case V4L2_CID_POWER_LINE_FREQUENCY: - return camera_power_line_frequency; - case V4L2_CID_EXPOSURE_AUTO: - return camera_exposure_auto; - case V4L2_CID_COLORFX: - return colorfx; - case V4L2_CID_TUNE_PREEMPHASIS: - return tune_preemphasis; - default: - return NULL; - } -} -EXPORT_SYMBOL(v4l2_ctrl_get_menu); - -/* Return the control name. */ -const char *v4l2_ctrl_get_name(u32 id) -{ - switch (id) { - /* USER controls */ - case V4L2_CID_USER_CLASS: return "User Controls"; - case V4L2_CID_BRIGHTNESS: return "Brightness"; - case V4L2_CID_CONTRAST: return "Contrast"; - case V4L2_CID_SATURATION: return "Saturation"; - case V4L2_CID_HUE: return "Hue"; - case V4L2_CID_AUDIO_VOLUME: return "Volume"; - case V4L2_CID_AUDIO_BALANCE: return "Balance"; - case V4L2_CID_AUDIO_BASS: return "Bass"; - case V4L2_CID_AUDIO_TREBLE: return "Treble"; - case V4L2_CID_AUDIO_MUTE: return "Mute"; - case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; - case V4L2_CID_BLACK_LEVEL: return "Black Level"; - case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; - case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; - case V4L2_CID_RED_BALANCE: return "Red Balance"; - case V4L2_CID_BLUE_BALANCE: return "Blue Balance"; - case V4L2_CID_GAMMA: return "Gamma"; - case V4L2_CID_EXPOSURE: return "Exposure"; - case V4L2_CID_AUTOGAIN: return "Gain, Automatic"; - case V4L2_CID_GAIN: return "Gain"; - case V4L2_CID_HFLIP: return "Horizontal Flip"; - case V4L2_CID_VFLIP: return "Vertical Flip"; - case V4L2_CID_HCENTER: return "Horizontal Center"; - case V4L2_CID_VCENTER: return "Vertical Center"; - case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; - case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; - case V4L2_CID_SHARPNESS: return "Sharpness"; - case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; - case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; - case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; - case V4L2_CID_COLOR_KILLER: return "Color Killer"; - case V4L2_CID_COLORFX: return "Color Effects"; - case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic"; - case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter"; - case V4L2_CID_ROTATE: return "Rotate"; - case V4L2_CID_BG_COLOR: return "Background Color"; - - /* MPEG controls */ - case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency"; - case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding"; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate"; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate"; - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate"; - case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; - case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode"; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension"; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis"; - case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC"; - case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; - case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; - case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size"; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure"; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown"; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode"; - case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate"; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate"; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation"; - case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute"; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV"; - case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; - case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; - case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; - case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID"; - case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID"; - case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID"; - case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; - case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; - - /* CAMERA controls */ - case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; - case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; - case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; - case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate"; - case V4L2_CID_PAN_RELATIVE: return "Pan, Relative"; - case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative"; - case V4L2_CID_PAN_RESET: return "Pan, Reset"; - case V4L2_CID_TILT_RESET: return "Tilt, Reset"; - case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute"; - case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; - case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; - case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; - case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; - case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; - case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; - case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; - case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; - case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; - case V4L2_CID_PRIVACY: return "Privacy"; - - /* FM Radio Modulator control */ - case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; - case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; - case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; - case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; - case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; - case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; - case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; - case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; - case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled"; - case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain"; - case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold"; - case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time"; - case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time"; - case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled"; - case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation"; - case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency"; - case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings"; - case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level"; - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; - - default: - return NULL; - } -} -EXPORT_SYMBOL(v4l2_ctrl_get_name); - /* Fill in a struct v4l2_queryctrl */ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) { - const char *name = v4l2_ctrl_get_name(qctrl->id); + const char *name; + + v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type, + &min, &max, &step, &def, &qctrl->flags); - qctrl->flags = 0; if (name == NULL) return -EINVAL; - switch (qctrl->id) { - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_LOUDNESS: - case V4L2_CID_AUTO_WHITE_BALANCE: - case V4L2_CID_AUTOGAIN: - case V4L2_CID_HFLIP: - case V4L2_CID_VFLIP: - case V4L2_CID_HUE_AUTO: - case V4L2_CID_CHROMA_AGC: - case V4L2_CID_COLOR_KILLER: - case V4L2_CID_MPEG_AUDIO_MUTE: - case V4L2_CID_MPEG_VIDEO_MUTE: - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - case V4L2_CID_EXPOSURE_AUTO_PRIORITY: - case V4L2_CID_FOCUS_AUTO: - case V4L2_CID_PRIVACY: - case V4L2_CID_AUDIO_LIMITER_ENABLED: - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: - case V4L2_CID_PILOT_TONE_ENABLED: - qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; - min = 0; - max = step = 1; - break; - case V4L2_CID_PAN_RESET: - case V4L2_CID_TILT_RESET: - qctrl->type = V4L2_CTRL_TYPE_BUTTON; - qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - min = max = step = def = 0; - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - case V4L2_CID_MPEG_AUDIO_ENCODING: - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - case V4L2_CID_MPEG_AUDIO_MODE: - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - case V4L2_CID_MPEG_AUDIO_CRC: - case V4L2_CID_MPEG_VIDEO_ENCODING: - case V4L2_CID_MPEG_VIDEO_ASPECT: - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - case V4L2_CID_MPEG_STREAM_TYPE: - case V4L2_CID_MPEG_STREAM_VBI_FMT: - case V4L2_CID_EXPOSURE_AUTO: - case V4L2_CID_COLORFX: - case V4L2_CID_TUNE_PREEMPHASIS: - qctrl->type = V4L2_CTRL_TYPE_MENU; - step = 1; - break; - case V4L2_CID_RDS_TX_PS_NAME: - case V4L2_CID_RDS_TX_RADIO_TEXT: - qctrl->type = V4L2_CTRL_TYPE_STRING; - break; - case V4L2_CID_USER_CLASS: - case V4L2_CID_CAMERA_CLASS: - case V4L2_CID_MPEG_CLASS: - case V4L2_CID_FM_TX_CLASS: - qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS; - qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - min = max = step = def = 0; - break; - case V4L2_CID_BG_COLOR: - qctrl->type = V4L2_CTRL_TYPE_INTEGER; - step = 1; - min = 0; - /* Max is calculated as RGB888 that is 2^24 */ - max = 0xFFFFFF; - break; - default: - qctrl->type = V4L2_CTRL_TYPE_INTEGER; - break; - } - switch (qctrl->id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - case V4L2_CID_MPEG_AUDIO_MODE: - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - case V4L2_CID_MPEG_STREAM_TYPE: - qctrl->flags |= V4L2_CTRL_FLAG_UPDATE; - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case V4L2_CID_GAMMA: - case V4L2_CID_SHARPNESS: - case V4L2_CID_CHROMA_GAIN: - case V4L2_CID_RDS_TX_DEVIATION: - case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: - case V4L2_CID_AUDIO_LIMITER_DEVIATION: - case V4L2_CID_AUDIO_COMPRESSION_GAIN: - case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: - case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: - case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: - case V4L2_CID_PILOT_TONE_DEVIATION: - case V4L2_CID_PILOT_TONE_FREQUENCY: - case V4L2_CID_TUNE_POWER_LEVEL: - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; - break; - case V4L2_CID_PAN_RELATIVE: - case V4L2_CID_TILT_RELATIVE: - case V4L2_CID_FOCUS_RELATIVE: - case V4L2_CID_IRIS_RELATIVE: - case V4L2_CID_ZOOM_RELATIVE: - qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - break; - } qctrl->minimum = min; qctrl->maximum = max; qctrl->step = step; @@ -850,7 +381,8 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, /* Create the i2c client */ if (info->addr == 0 && probe_addrs) - client = i2c_new_probed_device(adapter, info, probe_addrs); + client = i2c_new_probed_device(adapter, info, probe_addrs, + NULL); else client = i2c_new_device(adapter, info); diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 9004a5fe764..073f01390cd 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -5,7 +5,7 @@ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs - * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> * @@ -228,11 +228,6 @@ static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (file->f_op->unlocked_ioctl) ret = file->f_op->unlocked_ioctl(file, cmd, arg); - else if (file->f_op->ioctl) { - lock_kernel(); - ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - unlock_kernel(); - } return ret; } @@ -973,7 +968,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { long ret = -ENOIOCTLCMD; - if (!file->f_op->ioctl && !file->f_op->unlocked_ioctl) + if (!file->f_op->unlocked_ioctl) return ret; switch (cmd) { diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c new file mode 100644 index 00000000000..ea8d32cd425 --- /dev/null +++ b/drivers/media/video/v4l2-ctrls.c @@ -0,0 +1,1852 @@ +/* + V4L2 controls framework implementation. + + Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/ctype.h> +#include <linux/slab.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> + +/* Internal temporary helper struct, one for each v4l2_ext_control */ +struct ctrl_helper { + /* The control corresponding to the v4l2_ext_control ID field. */ + struct v4l2_ctrl *ctrl; + /* Used internally to mark whether this control was already + processed. */ + bool handled; +}; + +/* Returns NULL or a character pointer array containing the menu for + the given control ID. The pointer array ends with a NULL pointer. + An empty string signifies a menu entry that is invalid. This allows + drivers to disable certain options if it is not supported. */ +const char **v4l2_ctrl_get_menu(u32 id) +{ + static const char *mpeg_audio_sampling_freq[] = { + "44.1 kHz", + "48 kHz", + "32 kHz", + NULL + }; + static const char *mpeg_audio_encoding[] = { + "MPEG-1/2 Layer I", + "MPEG-1/2 Layer II", + "MPEG-1/2 Layer III", + "MPEG-2/4 AAC", + "AC-3", + NULL + }; + static const char *mpeg_audio_l1_bitrate[] = { + "32 kbps", + "64 kbps", + "96 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "288 kbps", + "320 kbps", + "352 kbps", + "384 kbps", + "416 kbps", + "448 kbps", + NULL + }; + static const char *mpeg_audio_l2_bitrate[] = { + "32 kbps", + "48 kbps", + "56 kbps", + "64 kbps", + "80 kbps", + "96 kbps", + "112 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "320 kbps", + "384 kbps", + NULL + }; + static const char *mpeg_audio_l3_bitrate[] = { + "32 kbps", + "40 kbps", + "48 kbps", + "56 kbps", + "64 kbps", + "80 kbps", + "96 kbps", + "112 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "320 kbps", + NULL + }; + static const char *mpeg_audio_ac3_bitrate[] = { + "32 kbps", + "40 kbps", + "48 kbps", + "56 kbps", + "64 kbps", + "80 kbps", + "96 kbps", + "112 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "320 kbps", + "384 kbps", + "448 kbps", + "512 kbps", + "576 kbps", + "640 kbps", + NULL + }; + static const char *mpeg_audio_mode[] = { + "Stereo", + "Joint Stereo", + "Dual", + "Mono", + NULL + }; + static const char *mpeg_audio_mode_extension[] = { + "Bound 4", + "Bound 8", + "Bound 12", + "Bound 16", + NULL + }; + static const char *mpeg_audio_emphasis[] = { + "No Emphasis", + "50/15 us", + "CCITT J17", + NULL + }; + static const char *mpeg_audio_crc[] = { + "No CRC", + "16-bit CRC", + NULL + }; + static const char *mpeg_video_encoding[] = { + "MPEG-1", + "MPEG-2", + "MPEG-4 AVC", + NULL + }; + static const char *mpeg_video_aspect[] = { + "1x1", + "4x3", + "16x9", + "2.21x1", + NULL + }; + static const char *mpeg_video_bitrate_mode[] = { + "Variable Bitrate", + "Constant Bitrate", + NULL + }; + static const char *mpeg_stream_type[] = { + "MPEG-2 Program Stream", + "MPEG-2 Transport Stream", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + static const char *mpeg_stream_vbi_fmt[] = { + "No VBI", + "Private packet, IVTV format", + NULL + }; + static const char *camera_power_line_frequency[] = { + "Disabled", + "50 Hz", + "60 Hz", + NULL + }; + static const char *camera_exposure_auto[] = { + "Auto Mode", + "Manual Mode", + "Shutter Priority Mode", + "Aperture Priority Mode", + NULL + }; + static const char *colorfx[] = { + "None", + "Black & White", + "Sepia", + "Negative", + "Emboss", + "Sketch", + "Sky blue", + "Grass green", + "Skin whiten", + "Vivid", + NULL + }; + static const char *tune_preemphasis[] = { + "No preemphasis", + "50 useconds", + "75 useconds", + NULL, + }; + + switch (id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return mpeg_audio_sampling_freq; + case V4L2_CID_MPEG_AUDIO_ENCODING: + return mpeg_audio_encoding; + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: + return mpeg_audio_l1_bitrate; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + return mpeg_audio_l2_bitrate; + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + return mpeg_audio_l3_bitrate; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return mpeg_audio_ac3_bitrate; + case V4L2_CID_MPEG_AUDIO_MODE: + return mpeg_audio_mode; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + return mpeg_audio_mode_extension; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + return mpeg_audio_emphasis; + case V4L2_CID_MPEG_AUDIO_CRC: + return mpeg_audio_crc; + case V4L2_CID_MPEG_VIDEO_ENCODING: + return mpeg_video_encoding; + case V4L2_CID_MPEG_VIDEO_ASPECT: + return mpeg_video_aspect; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return mpeg_video_bitrate_mode; + case V4L2_CID_MPEG_STREAM_TYPE: + return mpeg_stream_type; + case V4L2_CID_MPEG_STREAM_VBI_FMT: + return mpeg_stream_vbi_fmt; + case V4L2_CID_POWER_LINE_FREQUENCY: + return camera_power_line_frequency; + case V4L2_CID_EXPOSURE_AUTO: + return camera_exposure_auto; + case V4L2_CID_COLORFX: + return colorfx; + case V4L2_CID_TUNE_PREEMPHASIS: + return tune_preemphasis; + default: + return NULL; + } +} +EXPORT_SYMBOL(v4l2_ctrl_get_menu); + +/* Return the control name. */ +const char *v4l2_ctrl_get_name(u32 id) +{ + switch (id) { + /* USER controls */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; + case V4L2_CID_AUDIO_VOLUME: return "Volume"; + case V4L2_CID_AUDIO_BALANCE: return "Balance"; + case V4L2_CID_AUDIO_BASS: return "Bass"; + case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; + case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; + case V4L2_CID_BLACK_LEVEL: return "Black Level"; + case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; + case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; + case V4L2_CID_RED_BALANCE: return "Red Balance"; + case V4L2_CID_BLUE_BALANCE: return "Blue Balance"; + case V4L2_CID_GAMMA: return "Gamma"; + case V4L2_CID_EXPOSURE: return "Exposure"; + case V4L2_CID_AUTOGAIN: return "Gain, Automatic"; + case V4L2_CID_GAIN: return "Gain"; + case V4L2_CID_HFLIP: return "Horizontal Flip"; + case V4L2_CID_VFLIP: return "Vertical Flip"; + case V4L2_CID_HCENTER: return "Horizontal Center"; + case V4L2_CID_VCENTER: return "Vertical Center"; + case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; + case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; + case V4L2_CID_SHARPNESS: return "Sharpness"; + case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; + case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; + case V4L2_CID_COLOR_KILLER: return "Color Killer"; + case V4L2_CID_COLORFX: return "Color Effects"; + case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic"; + case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter"; + case V4L2_CID_ROTATE: return "Rotate"; + case V4L2_CID_BG_COLOR: return "Background Color"; + case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; + + /* MPEG controls */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; + case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; + case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID"; + case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID"; + case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID"; + case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; + case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency"; + case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding"; + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate"; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate"; + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate"; + case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode"; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension"; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis"; + case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC"; + case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; + case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; + case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size"; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure"; + case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown"; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode"; + case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate"; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate"; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation"; + case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute"; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV"; + + /* CAMERA controls */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; + case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; + case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; + case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate"; + case V4L2_CID_PAN_RELATIVE: return "Pan, Relative"; + case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative"; + case V4L2_CID_PAN_RESET: return "Pan, Reset"; + case V4L2_CID_TILT_RESET: return "Tilt, Reset"; + case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute"; + case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; + case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; + case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; + case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; + case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; + case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; + case V4L2_CID_PRIVACY: return "Privacy"; + case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; + case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; + + /* FM Radio Modulator control */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; + case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; + case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; + case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; + case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; + case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; + case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled"; + case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain"; + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold"; + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time"; + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time"; + case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled"; + case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation"; + case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency"; + case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings"; + case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level"; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; + + default: + return NULL; + } +} +EXPORT_SYMBOL(v4l2_ctrl_get_name); + +void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) +{ + *name = v4l2_ctrl_get_name(id); + *flags = 0; + + switch (id) { + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_LOUDNESS: + case V4L2_CID_AUTO_WHITE_BALANCE: + case V4L2_CID_AUTOGAIN: + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + case V4L2_CID_HUE_AUTO: + case V4L2_CID_CHROMA_AGC: + case V4L2_CID_COLOR_KILLER: + case V4L2_CID_MPEG_AUDIO_MUTE: + case V4L2_CID_MPEG_VIDEO_MUTE: + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + case V4L2_CID_MPEG_VIDEO_PULLDOWN: + case V4L2_CID_EXPOSURE_AUTO_PRIORITY: + case V4L2_CID_FOCUS_AUTO: + case V4L2_CID_PRIVACY: + case V4L2_CID_AUDIO_LIMITER_ENABLED: + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: + case V4L2_CID_PILOT_TONE_ENABLED: + *type = V4L2_CTRL_TYPE_BOOLEAN; + *min = 0; + *max = *step = 1; + break; + case V4L2_CID_PAN_RESET: + case V4L2_CID_TILT_RESET: + *type = V4L2_CTRL_TYPE_BUTTON; + *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + *min = *max = *step = *def = 0; + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + case V4L2_CID_MPEG_AUDIO_ENCODING: + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + case V4L2_CID_MPEG_AUDIO_MODE: + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + case V4L2_CID_MPEG_AUDIO_CRC: + case V4L2_CID_MPEG_VIDEO_ENCODING: + case V4L2_CID_MPEG_VIDEO_ASPECT: + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + case V4L2_CID_MPEG_STREAM_TYPE: + case V4L2_CID_MPEG_STREAM_VBI_FMT: + case V4L2_CID_EXPOSURE_AUTO: + case V4L2_CID_COLORFX: + case V4L2_CID_TUNE_PREEMPHASIS: + *type = V4L2_CTRL_TYPE_MENU; + break; + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + *type = V4L2_CTRL_TYPE_STRING; + break; + case V4L2_CID_USER_CLASS: + case V4L2_CID_CAMERA_CLASS: + case V4L2_CID_MPEG_CLASS: + case V4L2_CID_FM_TX_CLASS: + *type = V4L2_CTRL_TYPE_CTRL_CLASS; + /* You can neither read not write these */ + *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; + *min = *max = *step = *def = 0; + break; + case V4L2_CID_BG_COLOR: + *type = V4L2_CTRL_TYPE_INTEGER; + *step = 1; + *min = 0; + /* Max is calculated as RGB888 that is 2^24 */ + *max = 0xFFFFFF; + break; + default: + *type = V4L2_CTRL_TYPE_INTEGER; + break; + } + switch (id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + case V4L2_CID_MPEG_AUDIO_MODE: + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + case V4L2_CID_MPEG_STREAM_TYPE: + *flags |= V4L2_CTRL_FLAG_UPDATE; + break; + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case V4L2_CID_GAMMA: + case V4L2_CID_SHARPNESS: + case V4L2_CID_CHROMA_GAIN: + case V4L2_CID_RDS_TX_DEVIATION: + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: + case V4L2_CID_AUDIO_LIMITER_DEVIATION: + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: + case V4L2_CID_PILOT_TONE_DEVIATION: + case V4L2_CID_PILOT_TONE_FREQUENCY: + case V4L2_CID_TUNE_POWER_LEVEL: + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + *flags |= V4L2_CTRL_FLAG_SLIDER; + break; + case V4L2_CID_PAN_RELATIVE: + case V4L2_CID_TILT_RELATIVE: + case V4L2_CID_FOCUS_RELATIVE: + case V4L2_CID_IRIS_RELATIVE: + case V4L2_CID_ZOOM_RELATIVE: + *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + break; + } +} +EXPORT_SYMBOL(v4l2_ctrl_fill); + +/* Helper function to determine whether the control type is compatible with + VIDIOC_G/S_CTRL. */ +static bool type_is_int(const struct v4l2_ctrl *ctrl) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER64: + case V4L2_CTRL_TYPE_STRING: + /* Nope, these need v4l2_ext_control */ + return false; + default: + return true; + } +} + +/* Helper function: copy the current control value back to the caller */ +static int cur_to_user(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl) +{ + u32 len; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + len = strlen(ctrl->cur.string); + if (c->size < len + 1) { + c->size = len + 1; + return -ENOSPC; + } + return copy_to_user(c->string, ctrl->cur.string, + len + 1) ? -EFAULT : 0; + case V4L2_CTRL_TYPE_INTEGER64: + c->value64 = ctrl->cur.val64; + break; + default: + c->value = ctrl->cur.val; + break; + } + return 0; +} + +/* Helper function: copy the caller-provider value as the new control value */ +static int user_to_new(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl) +{ + int ret; + u32 size; + + ctrl->has_new = 1; + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER64: + ctrl->val64 = c->value64; + break; + case V4L2_CTRL_TYPE_STRING: + size = c->size; + if (size == 0) + return -ERANGE; + if (size > ctrl->maximum + 1) + size = ctrl->maximum + 1; + ret = copy_from_user(ctrl->string, c->string, size); + if (!ret) { + char last = ctrl->string[size - 1]; + + ctrl->string[size - 1] = 0; + /* If the string was longer than ctrl->maximum, + then return an error. */ + if (strlen(ctrl->string) == ctrl->maximum && last) + return -ERANGE; + } + return ret ? -EFAULT : 0; + default: + ctrl->val = c->value; + break; + } + return 0; +} + +/* Helper function: copy the new control value back to the caller */ +static int new_to_user(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl) +{ + u32 len; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + len = strlen(ctrl->string); + if (c->size < len + 1) { + c->size = ctrl->maximum + 1; + return -ENOSPC; + } + return copy_to_user(c->string, ctrl->string, + len + 1) ? -EFAULT : 0; + case V4L2_CTRL_TYPE_INTEGER64: + c->value64 = ctrl->val64; + break; + default: + c->value = ctrl->val; + break; + } + return 0; +} + +/* Copy the new value to the current value. */ +static void new_to_cur(struct v4l2_ctrl *ctrl) +{ + if (ctrl == NULL) + return; + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + /* strings are always 0-terminated */ + strcpy(ctrl->cur.string, ctrl->string); + break; + case V4L2_CTRL_TYPE_INTEGER64: + ctrl->cur.val64 = ctrl->val64; + break; + default: + ctrl->cur.val = ctrl->val; + break; + } +} + +/* Copy the current value to the new value */ +static void cur_to_new(struct v4l2_ctrl *ctrl) +{ + if (ctrl == NULL) + return; + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + /* strings are always 0-terminated */ + strcpy(ctrl->string, ctrl->cur.string); + break; + case V4L2_CTRL_TYPE_INTEGER64: + ctrl->val64 = ctrl->cur.val64; + break; + default: + ctrl->val = ctrl->cur.val; + break; + } +} + +/* Return non-zero if one or more of the controls in the cluster has a new + value that differs from the current value. */ +static int cluster_changed(struct v4l2_ctrl *master) +{ + int diff = 0; + int i; + + for (i = 0; !diff && i < master->ncontrols; i++) { + struct v4l2_ctrl *ctrl = master->cluster[i]; + + if (ctrl == NULL) + continue; + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BUTTON: + /* Button controls are always 'different' */ + return 1; + case V4L2_CTRL_TYPE_STRING: + /* strings are always 0-terminated */ + diff = strcmp(ctrl->string, ctrl->cur.string); + break; + case V4L2_CTRL_TYPE_INTEGER64: + diff = ctrl->val64 != ctrl->cur.val64; + break; + default: + diff = ctrl->val != ctrl->cur.val; + break; + } + } + return diff; +} + +/* Validate a new control */ +static int validate_new(struct v4l2_ctrl *ctrl) +{ + s32 val = ctrl->val; + char *s = ctrl->string; + u32 offset; + size_t len; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + /* Round towards the closest legal value */ + val += ctrl->step / 2; + if (val < ctrl->minimum) + val = ctrl->minimum; + if (val > ctrl->maximum) + val = ctrl->maximum; + offset = val - ctrl->minimum; + offset = ctrl->step * (offset / ctrl->step); + val = ctrl->minimum + offset; + ctrl->val = val; + return 0; + + case V4L2_CTRL_TYPE_BOOLEAN: + ctrl->val = !!ctrl->val; + return 0; + + case V4L2_CTRL_TYPE_MENU: + if (val < ctrl->minimum || val > ctrl->maximum) + return -ERANGE; + if (ctrl->qmenu[val][0] == '\0' || + (ctrl->menu_skip_mask & (1 << val))) + return -EINVAL; + return 0; + + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + ctrl->val64 = 0; + return 0; + + case V4L2_CTRL_TYPE_INTEGER64: + return 0; + + case V4L2_CTRL_TYPE_STRING: + len = strlen(s); + if (len < ctrl->minimum) + return -ERANGE; + if ((len - ctrl->minimum) % ctrl->step) + return -ERANGE; + return 0; + + default: + return -EINVAL; + } +} + +static inline u32 node2id(struct list_head *node) +{ + return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id; +} + +/* Set the handler's error code if it wasn't set earlier already */ +static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) +{ + if (hdl->error == 0) + hdl->error = err; + return err; +} + +/* Initialize the handler */ +int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, + unsigned nr_of_controls_hint) +{ + mutex_init(&hdl->lock); + INIT_LIST_HEAD(&hdl->ctrls); + INIT_LIST_HEAD(&hdl->ctrl_refs); + hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; + hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets, + GFP_KERNEL); + hdl->error = hdl->buckets ? 0 : -ENOMEM; + return hdl->error; +} +EXPORT_SYMBOL(v4l2_ctrl_handler_init); + +/* Free all controls and control refs */ +void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) +{ + struct v4l2_ctrl_ref *ref, *next_ref; + struct v4l2_ctrl *ctrl, *next_ctrl; + + if (hdl == NULL || hdl->buckets == NULL) + return; + + mutex_lock(&hdl->lock); + /* Free all nodes */ + list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { + list_del(&ref->node); + kfree(ref); + } + /* Free all controls owned by the handler */ + list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) { + list_del(&ctrl->node); + kfree(ctrl); + } + kfree(hdl->buckets); + hdl->buckets = NULL; + hdl->cached = NULL; + hdl->error = 0; + mutex_unlock(&hdl->lock); +} +EXPORT_SYMBOL(v4l2_ctrl_handler_free); + +/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer + be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing + with applications that do not use the NEXT_CTRL flag. + + We just find the n-th private user control. It's O(N), but that should not + be an issue in this particular case. */ +static struct v4l2_ctrl_ref *find_private_ref( + struct v4l2_ctrl_handler *hdl, u32 id) +{ + struct v4l2_ctrl_ref *ref; + + id -= V4L2_CID_PRIVATE_BASE; + list_for_each_entry(ref, &hdl->ctrl_refs, node) { + /* Search for private user controls that are compatible with + VIDIOC_G/S_CTRL. */ + if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER && + V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { + if (!type_is_int(ref->ctrl)) + continue; + if (id == 0) + return ref; + id--; + } + } + return NULL; +} + +/* Find a control with the given ID. */ +static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id) +{ + struct v4l2_ctrl_ref *ref; + int bucket; + + id &= V4L2_CTRL_ID_MASK; + + /* Old-style private controls need special handling */ + if (id >= V4L2_CID_PRIVATE_BASE) + return find_private_ref(hdl, id); + bucket = id % hdl->nr_of_buckets; + + /* Simple optimization: cache the last control found */ + if (hdl->cached && hdl->cached->ctrl->id == id) + return hdl->cached; + + /* Not in cache, search the hash */ + ref = hdl->buckets ? hdl->buckets[bucket] : NULL; + while (ref && ref->ctrl->id != id) + ref = ref->next; + + if (ref) + hdl->cached = ref; /* cache it! */ + return ref; +} + +/* Find a control with the given ID. Take the handler's lock first. */ +static struct v4l2_ctrl_ref *find_ref_lock( + struct v4l2_ctrl_handler *hdl, u32 id) +{ + struct v4l2_ctrl_ref *ref = NULL; + + if (hdl) { + mutex_lock(&hdl->lock); + ref = find_ref(hdl, id); + mutex_unlock(&hdl->lock); + } + return ref; +} + +/* Find a control with the given ID. */ +struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id) +{ + struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id); + + return ref ? ref->ctrl : NULL; +} +EXPORT_SYMBOL(v4l2_ctrl_find); + +/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */ +static int handler_new_ref(struct v4l2_ctrl_handler *hdl, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl_ref *ref; + struct v4l2_ctrl_ref *new_ref; + u32 id = ctrl->id; + u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1; + int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ + + /* Automatically add the control class if it is not yet present. */ + if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) + if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0)) + return hdl->error; + + if (hdl->error) + return hdl->error; + + new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL); + if (!new_ref) + return handler_set_err(hdl, -ENOMEM); + new_ref->ctrl = ctrl; + if (ctrl->handler == hdl) { + /* By default each control starts in a cluster of its own. + new_ref->ctrl is basically a cluster array with one + element, so that's perfect to use as the cluster pointer. + But only do this for the handler that owns the control. */ + ctrl->cluster = &new_ref->ctrl; + ctrl->ncontrols = 1; + } + + INIT_LIST_HEAD(&new_ref->node); + + mutex_lock(&hdl->lock); + + /* Add immediately at the end of the list if the list is empty, or if + the last element in the list has a lower ID. + This ensures that when elements are added in ascending order the + insertion is an O(1) operation. */ + if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) { + list_add_tail(&new_ref->node, &hdl->ctrl_refs); + goto insert_in_hash; + } + + /* Find insert position in sorted list */ + list_for_each_entry(ref, &hdl->ctrl_refs, node) { + if (ref->ctrl->id < id) + continue; + /* Don't add duplicates */ + if (ref->ctrl->id == id) { + kfree(new_ref); + goto unlock; + } + list_add(&new_ref->node, ref->node.prev); + break; + } + +insert_in_hash: + /* Insert the control node in the hash */ + new_ref->next = hdl->buckets[bucket]; + hdl->buckets[bucket] = new_ref; + +unlock: + mutex_unlock(&hdl->lock); + return 0; +} + +/* Add a new control */ +static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, const char *name, enum v4l2_ctrl_type type, + s32 min, s32 max, u32 step, s32 def, + u32 flags, const char **qmenu, void *priv) +{ + struct v4l2_ctrl *ctrl; + unsigned sz_extra = 0; + + if (hdl->error) + return NULL; + + /* Sanity checks */ + if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || + def < min || def > max || max < min || + (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || + (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || + (type == V4L2_CTRL_TYPE_STRING && max == 0)) { + handler_set_err(hdl, -ERANGE); + return NULL; + } + + if (type == V4L2_CTRL_TYPE_BUTTON) + flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) + flags |= V4L2_CTRL_FLAG_READ_ONLY; + else if (type == V4L2_CTRL_TYPE_STRING) + sz_extra += 2 * (max + 1); + + ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); + if (ctrl == NULL) { + handler_set_err(hdl, -ENOMEM); + return NULL; + } + + INIT_LIST_HEAD(&ctrl->node); + ctrl->handler = hdl; + ctrl->ops = ops; + ctrl->id = id; + ctrl->name = name; + ctrl->type = type; + ctrl->flags = flags; + ctrl->minimum = min; + ctrl->maximum = max; + ctrl->step = step; + ctrl->qmenu = qmenu; + ctrl->priv = priv; + ctrl->cur.val = ctrl->val = ctrl->default_value = def; + + if (ctrl->type == V4L2_CTRL_TYPE_STRING) { + ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1); + ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1); + if (ctrl->minimum) + memset(ctrl->cur.string, ' ', ctrl->minimum); + } + if (handler_new_ref(hdl, ctrl)) { + kfree(ctrl); + return NULL; + } + mutex_lock(&hdl->lock); + list_add_tail(&ctrl->node, &hdl->ctrls); + mutex_unlock(&hdl->lock); + return ctrl; +} + +struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_config *cfg, void *priv) +{ + bool is_menu; + struct v4l2_ctrl *ctrl; + const char *name = cfg->name; + const char **qmenu = cfg->qmenu; + enum v4l2_ctrl_type type = cfg->type; + u32 flags = cfg->flags; + s32 min = cfg->min; + s32 max = cfg->max; + u32 step = cfg->step; + s32 def = cfg->def; + + if (name == NULL) + v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, + &def, &flags); + + is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU); + if (is_menu) + WARN_ON(step); + else + WARN_ON(cfg->menu_skip_mask); + if (is_menu && qmenu == NULL) + qmenu = v4l2_ctrl_get_menu(cfg->id); + + ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, + type, min, max, + is_menu ? cfg->menu_skip_mask : step, + def, flags, qmenu, priv); + if (ctrl) { + ctrl->is_private = cfg->is_private; + ctrl->is_volatile = cfg->is_volatile; + } + return ctrl; +} +EXPORT_SYMBOL(v4l2_ctrl_new_custom); + +/* Helper function for standard non-menu controls */ +struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 min, s32 max, u32 step, s32 def) +{ + const char *name; + enum v4l2_ctrl_type type; + u32 flags; + + v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); + if (type == V4L2_CTRL_TYPE_MENU) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + return v4l2_ctrl_new(hdl, ops, id, name, type, + min, max, step, def, flags, NULL, NULL); +} +EXPORT_SYMBOL(v4l2_ctrl_new_std); + +/* Helper function for standard menu controls */ +struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 mask, s32 def) +{ + const char **qmenu = v4l2_ctrl_get_menu(id); + const char *name; + enum v4l2_ctrl_type type; + s32 min; + s32 step; + u32 flags; + + v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); + if (type != V4L2_CTRL_TYPE_MENU) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + return v4l2_ctrl_new(hdl, ops, id, name, type, + 0, max, mask, def, flags, qmenu, NULL); +} +EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); + +/* Add a control from another handler to this handler */ +struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, + struct v4l2_ctrl *ctrl) +{ + if (hdl == NULL || hdl->error) + return NULL; + if (ctrl == NULL) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + if (ctrl->handler == hdl) + return ctrl; + return handler_new_ref(hdl, ctrl) ? NULL : ctrl; +} +EXPORT_SYMBOL(v4l2_ctrl_add_ctrl); + +/* Add the controls from another handler to our own. */ +int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, + struct v4l2_ctrl_handler *add) +{ + struct v4l2_ctrl *ctrl; + int ret = 0; + + /* Do nothing if either handler is NULL or if they are the same */ + if (!hdl || !add || hdl == add) + return 0; + if (hdl->error) + return hdl->error; + mutex_lock(&add->lock); + list_for_each_entry(ctrl, &add->ctrls, node) { + /* Skip handler-private controls. */ + if (ctrl->is_private) + continue; + ret = handler_new_ref(hdl, ctrl); + if (ret) + break; + } + mutex_unlock(&add->lock); + return ret; +} +EXPORT_SYMBOL(v4l2_ctrl_add_handler); + +/* Cluster controls */ +void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls) +{ + int i; + + /* The first control is the master control and it must not be NULL */ + BUG_ON(controls[0] == NULL); + + for (i = 0; i < ncontrols; i++) { + if (controls[i]) { + controls[i]->cluster = controls; + controls[i]->ncontrols = ncontrols; + } + } +} +EXPORT_SYMBOL(v4l2_ctrl_cluster); + +/* Activate/deactivate a control. */ +void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active) +{ + if (ctrl == NULL) + return; + + if (!active) + /* set V4L2_CTRL_FLAG_INACTIVE */ + set_bit(4, &ctrl->flags); + else + /* clear V4L2_CTRL_FLAG_INACTIVE */ + clear_bit(4, &ctrl->flags); +} +EXPORT_SYMBOL(v4l2_ctrl_activate); + +/* Grab/ungrab a control. + Typically used when streaming starts and you want to grab controls, + preventing the user from changing them. + + Just call this and the framework will block any attempts to change + these controls. */ +void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed) +{ + if (ctrl == NULL) + return; + + if (grabbed) + /* set V4L2_CTRL_FLAG_GRABBED */ + set_bit(1, &ctrl->flags); + else + /* clear V4L2_CTRL_FLAG_GRABBED */ + clear_bit(1, &ctrl->flags); +} +EXPORT_SYMBOL(v4l2_ctrl_grab); + +/* Log the control name and value */ +static void log_ctrl(const struct v4l2_ctrl *ctrl, + const char *prefix, const char *colon) +{ + int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE; + int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED; + + if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY)) + return; + if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) + return; + + printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name); + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + printk(KERN_CONT "%d", ctrl->cur.val); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false"); + break; + case V4L2_CTRL_TYPE_MENU: + printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); + break; + case V4L2_CTRL_TYPE_INTEGER64: + printk(KERN_CONT "%lld", ctrl->cur.val64); + break; + case V4L2_CTRL_TYPE_STRING: + printk(KERN_CONT "%s", ctrl->cur.string); + break; + default: + printk(KERN_CONT "unknown type %d", ctrl->type); + break; + } + if (fl_inact && fl_grabbed) + printk(KERN_CONT " (inactive, grabbed)\n"); + else if (fl_inact) + printk(KERN_CONT " (inactive)\n"); + else if (fl_grabbed) + printk(KERN_CONT " (grabbed)\n"); + else + printk(KERN_CONT "\n"); +} + +/* Log all controls owned by the handler */ +void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, + const char *prefix) +{ + struct v4l2_ctrl *ctrl; + const char *colon = ""; + int len; + + if (hdl == NULL) + return; + if (prefix == NULL) + prefix = ""; + len = strlen(prefix); + if (len && prefix[len - 1] != ' ') + colon = ": "; + mutex_lock(&hdl->lock); + list_for_each_entry(ctrl, &hdl->ctrls, node) + if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED)) + log_ctrl(ctrl, prefix, colon); + mutex_unlock(&hdl->lock); +} +EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); + +/* Call s_ctrl for all controls owned by the handler */ +int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) +{ + struct v4l2_ctrl *ctrl; + int ret = 0; + + if (hdl == NULL) + return 0; + mutex_lock(&hdl->lock); + list_for_each_entry(ctrl, &hdl->ctrls, node) + ctrl->done = false; + + list_for_each_entry(ctrl, &hdl->ctrls, node) { + struct v4l2_ctrl *master = ctrl->cluster[0]; + int i; + + /* Skip if this control was already handled by a cluster. */ + if (ctrl->done) + continue; + + for (i = 0; i < master->ncontrols; i++) + cur_to_new(master->cluster[i]); + + /* Skip button controls and read-only controls. */ + if (ctrl->type == V4L2_CTRL_TYPE_BUTTON || + (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) + continue; + ret = master->ops->s_ctrl(master); + if (ret) + break; + for (i = 0; i < master->ncontrols; i++) + if (master->cluster[i]) + master->cluster[i]->done = true; + } + mutex_unlock(&hdl->lock); + return ret; +} +EXPORT_SYMBOL(v4l2_ctrl_handler_setup); + +/* Implement VIDIOC_QUERYCTRL */ +int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +{ + u32 id = qc->id & V4L2_CTRL_ID_MASK; + struct v4l2_ctrl_ref *ref; + struct v4l2_ctrl *ctrl; + + if (hdl == NULL) + return -EINVAL; + + mutex_lock(&hdl->lock); + + /* Try to find it */ + ref = find_ref(hdl, id); + + if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) { + /* Find the next control with ID > qc->id */ + + /* Did we reach the end of the control list? */ + if (id >= node2id(hdl->ctrl_refs.prev)) { + ref = NULL; /* Yes, so there is no next control */ + } else if (ref) { + /* We found a control with the given ID, so just get + the next one in the list. */ + ref = list_entry(ref->node.next, typeof(*ref), node); + } else { + /* No control with the given ID exists, so start + searching for the next largest ID. We know there + is one, otherwise the first 'if' above would have + been true. */ + list_for_each_entry(ref, &hdl->ctrl_refs, node) + if (id < ref->ctrl->id) + break; + } + } + mutex_unlock(&hdl->lock); + if (!ref) + return -EINVAL; + + ctrl = ref->ctrl; + memset(qc, 0, sizeof(*qc)); + qc->id = ctrl->id; + strlcpy(qc->name, ctrl->name, sizeof(qc->name)); + qc->minimum = ctrl->minimum; + qc->maximum = ctrl->maximum; + qc->default_value = ctrl->default_value; + if (qc->type == V4L2_CTRL_TYPE_MENU) + qc->step = 1; + else + qc->step = ctrl->step; + qc->flags = ctrl->flags; + qc->type = ctrl->type; + return 0; +} +EXPORT_SYMBOL(v4l2_queryctrl); + +int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + return v4l2_queryctrl(sd->ctrl_handler, qc); +} +EXPORT_SYMBOL(v4l2_subdev_queryctrl); + +/* Implement VIDIOC_QUERYMENU */ +int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) +{ + struct v4l2_ctrl *ctrl; + u32 i = qm->index; + + ctrl = v4l2_ctrl_find(hdl, qm->id); + if (!ctrl) + return -EINVAL; + + qm->reserved = 0; + /* Sanity checks */ + if (ctrl->qmenu == NULL || + i < ctrl->minimum || i > ctrl->maximum) + return -EINVAL; + /* Use mask to see if this menu item should be skipped */ + if (ctrl->menu_skip_mask & (1 << i)) + return -EINVAL; + /* Empty menu items should also be skipped */ + if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') + return -EINVAL; + strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + return 0; +} +EXPORT_SYMBOL(v4l2_querymenu); + +int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm) +{ + return v4l2_querymenu(sd->ctrl_handler, qm); +} +EXPORT_SYMBOL(v4l2_subdev_querymenu); + + + +/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS: + + It is not a fully atomic operation, just best-effort only. After all, if + multiple controls have to be set through multiple i2c writes (for example) + then some initial writes may succeed while others fail. Thus leaving the + system in an inconsistent state. The question is how much effort you are + willing to spend on trying to make something atomic that really isn't. + + From the point of view of an application the main requirement is that + when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an + error should be returned without actually affecting any controls. + + If all the values are correct, then it is acceptable to just give up + in case of low-level errors. + + It is important though that the application can tell when only a partial + configuration was done. The way we do that is through the error_idx field + of struct v4l2_ext_controls: if that is equal to the count field then no + controls were affected. Otherwise all controls before that index were + successful in performing their 'get' or 'set' operation, the control at + the given index failed, and you don't know what happened with the controls + after the failed one. Since if they were part of a control cluster they + could have been successfully processed (if a cluster member was encountered + at index < error_idx), they could have failed (if a cluster member was at + error_idx), or they may not have been processed yet (if the first cluster + member appeared after error_idx). + + It is all fairly theoretical, though. In practice all you can do is to + bail out. If error_idx == count, then it is an application bug. If + error_idx < count then it is only an application bug if the error code was + EBUSY. That usually means that something started streaming just when you + tried to set the controls. In all other cases it is a driver/hardware + problem and all you can do is to retry or bail out. + + Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that + never modifies controls the error_idx is just set to whatever control + has an invalid value. + */ + +/* Prepare for the extended g/s/try functions. + Find the controls in the control array and do some basic checks. */ +static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, + struct v4l2_ext_controls *cs, + struct ctrl_helper *helpers, + bool try) +{ + u32 i; + + for (i = 0; i < cs->count; i++) { + struct v4l2_ext_control *c = &cs->controls[i]; + struct v4l2_ctrl *ctrl; + u32 id = c->id & V4L2_CTRL_ID_MASK; + + if (try) + cs->error_idx = i; + + if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class) + return -EINVAL; + + /* Old-style private controls are not allowed for + extended controls */ + if (id >= V4L2_CID_PRIVATE_BASE) + return -EINVAL; + ctrl = v4l2_ctrl_find(hdl, id); + if (ctrl == NULL) + return -EINVAL; + if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + + helpers[i].ctrl = ctrl; + helpers[i].handled = false; + } + return 0; +} + +typedef int (*cluster_func)(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl); + +/* Walk over all controls in v4l2_ext_controls belonging to the same cluster + and call the provided function. */ +static int cluster_walk(unsigned from, + struct v4l2_ext_controls *cs, + struct ctrl_helper *helpers, + cluster_func f) +{ + struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster; + int ret = 0; + int i; + + /* Find any controls from the same cluster and call the function */ + for (i = from; !ret && i < cs->count; i++) { + struct v4l2_ctrl *ctrl = helpers[i].ctrl; + + if (!helpers[i].handled && ctrl->cluster == cluster) + ret = f(&cs->controls[i], ctrl); + } + return ret; +} + +static void cluster_done(unsigned from, + struct v4l2_ext_controls *cs, + struct ctrl_helper *helpers) +{ + struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster; + int i; + + /* Find any controls from the same cluster and mark them as handled */ + for (i = from; i < cs->count; i++) + if (helpers[i].ctrl->cluster == cluster) + helpers[i].handled = true; +} + +/* Handles the corner case where cs->count == 0. It checks whether the + specified control class exists. If that class ID is 0, then it checks + whether there are any controls at all. */ +static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class) +{ + if (ctrl_class == 0) + return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0; + return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL; +} + + + +/* Get extended controls. Allocates the helpers array if needed. */ +int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) +{ + struct ctrl_helper helper[4]; + struct ctrl_helper *helpers = helper; + int ret; + int i; + + cs->error_idx = cs->count; + cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class); + + if (hdl == NULL) + return -EINVAL; + + if (cs->count == 0) + return class_check(hdl, cs->ctrl_class); + + if (cs->count > ARRAY_SIZE(helper)) { + helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + if (helpers == NULL) + return -ENOMEM; + } + + ret = prepare_ext_ctrls(hdl, cs, helpers, false); + + for (i = 0; !ret && i < cs->count; i++) + if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) + ret = -EACCES; + + for (i = 0; !ret && i < cs->count; i++) { + struct v4l2_ctrl *ctrl = helpers[i].ctrl; + struct v4l2_ctrl *master = ctrl->cluster[0]; + + if (helpers[i].handled) + continue; + + cs->error_idx = i; + + v4l2_ctrl_lock(master); + /* g_volatile_ctrl will update the current control values */ + if (ctrl->is_volatile && master->ops->g_volatile_ctrl) + ret = master->ops->g_volatile_ctrl(master); + /* If OK, then copy the current control values to the caller */ + if (!ret) + ret = cluster_walk(i, cs, helpers, cur_to_user); + v4l2_ctrl_unlock(master); + cluster_done(i, cs, helpers); + } + + if (cs->count > ARRAY_SIZE(helper)) + kfree(helpers); + return ret; +} +EXPORT_SYMBOL(v4l2_g_ext_ctrls); + +int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs) +{ + return v4l2_g_ext_ctrls(sd->ctrl_handler, cs); +} +EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls); + +/* Helper function to get a single control */ +static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val) +{ + struct v4l2_ctrl *master = ctrl->cluster[0]; + int ret = 0; + + if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) + return -EACCES; + + v4l2_ctrl_lock(master); + /* g_volatile_ctrl will update the current control values */ + if (ctrl->is_volatile && master->ops->g_volatile_ctrl) + ret = master->ops->g_volatile_ctrl(master); + *val = ctrl->cur.val; + v4l2_ctrl_unlock(master); + return ret; +} + +int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control) +{ + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id); + + if (ctrl == NULL || !type_is_int(ctrl)) + return -EINVAL; + return get_ctrl(ctrl, &control->value); +} +EXPORT_SYMBOL(v4l2_g_ctrl); + +int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) +{ + return v4l2_g_ctrl(sd->ctrl_handler, control); +} +EXPORT_SYMBOL(v4l2_subdev_g_ctrl); + +s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl) +{ + s32 val = 0; + + /* It's a driver bug if this happens. */ + WARN_ON(!type_is_int(ctrl)); + get_ctrl(ctrl, &val); + return val; +} +EXPORT_SYMBOL(v4l2_ctrl_g_ctrl); + + +/* Core function that calls try/s_ctrl and ensures that the new value is + copied to the current value on a set. + Must be called with ctrl->handler->lock held. */ +static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set) +{ + bool try = !set; + int ret = 0; + int i; + + /* Go through the cluster and either validate the new value or + (if no new value was set), copy the current value to the new + value, ensuring a consistent view for the control ops when + called. */ + for (i = 0; !ret && i < master->ncontrols; i++) { + struct v4l2_ctrl *ctrl = master->cluster[i]; + + if (ctrl == NULL) + continue; + + if (ctrl->has_new) { + /* Double check this: it may have changed since the + last check in try_or_set_ext_ctrls(). */ + if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)) + return -EBUSY; + + /* Validate if required */ + if (!set) + ret = validate_new(ctrl); + continue; + } + /* No new value was set, so copy the current and force + a call to try_ctrl later, since the values for the cluster + may now have changed and the end result might be invalid. */ + try = true; + cur_to_new(ctrl); + } + + /* For larger clusters you have to call try_ctrl again to + verify that the controls are still valid after the + 'cur_to_new' above. */ + if (!ret && master->ops->try_ctrl && try) + ret = master->ops->try_ctrl(master); + + /* Don't set if there is no change */ + if (!ret && set && cluster_changed(master)) { + ret = master->ops->s_ctrl(master); + /* If OK, then make the new values permanent. */ + if (!ret) + for (i = 0; i < master->ncontrols; i++) + new_to_cur(master->cluster[i]); + } + return ret; +} + +/* Try or set controls. */ +static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl, + struct v4l2_ext_controls *cs, + struct ctrl_helper *helpers, + bool set) +{ + unsigned i, j; + int ret = 0; + + cs->error_idx = cs->count; + for (i = 0; i < cs->count; i++) { + struct v4l2_ctrl *ctrl = helpers[i].ctrl; + + if (!set) + cs->error_idx = i; + + if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) + return -EACCES; + /* This test is also done in try_set_control_cluster() which + is called in atomic context, so that has the final say, + but it makes sense to do an up-front check as well. Once + an error occurs in try_set_control_cluster() some other + controls may have been set already and we want to do a + best-effort to avoid that. */ + if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)) + return -EBUSY; + } + + for (i = 0; !ret && i < cs->count; i++) { + struct v4l2_ctrl *ctrl = helpers[i].ctrl; + struct v4l2_ctrl *master = ctrl->cluster[0]; + + cs->error_idx = i; + + if (helpers[i].handled) + continue; + + v4l2_ctrl_lock(ctrl); + + /* Reset the 'has_new' flags of the cluster */ + for (j = 0; j < master->ncontrols; j++) + if (master->cluster[j]) + master->cluster[j]->has_new = 0; + + /* Copy the new caller-supplied control values. + user_to_new() sets 'has_new' to 1. */ + ret = cluster_walk(i, cs, helpers, user_to_new); + + if (!ret) + ret = try_or_set_control_cluster(master, set); + + /* Copy the new values back to userspace. */ + if (!ret) + ret = cluster_walk(i, cs, helpers, new_to_user); + + v4l2_ctrl_unlock(ctrl); + cluster_done(i, cs, helpers); + } + return ret; +} + +/* Try or try-and-set controls */ +static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl, + struct v4l2_ext_controls *cs, + bool set) +{ + struct ctrl_helper helper[4]; + struct ctrl_helper *helpers = helper; + int ret; + int i; + + cs->error_idx = cs->count; + cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class); + + if (hdl == NULL) + return -EINVAL; + + if (cs->count == 0) + return class_check(hdl, cs->ctrl_class); + + if (cs->count > ARRAY_SIZE(helper)) { + helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + if (!helpers) + return -ENOMEM; + } + ret = prepare_ext_ctrls(hdl, cs, helpers, !set); + if (ret) + goto free; + + /* First 'try' all controls and abort on error */ + ret = try_or_set_ext_ctrls(hdl, cs, helpers, false); + /* If this is a 'set' operation and the initial 'try' failed, + then set error_idx to count to tell the application that no + controls changed value yet. */ + if (set) + cs->error_idx = cs->count; + if (!ret && set) { + /* Reset 'handled' state */ + for (i = 0; i < cs->count; i++) + helpers[i].handled = false; + ret = try_or_set_ext_ctrls(hdl, cs, helpers, true); + } + +free: + if (cs->count > ARRAY_SIZE(helper)) + kfree(helpers); + return ret; +} + +int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) +{ + return try_set_ext_ctrls(hdl, cs, false); +} +EXPORT_SYMBOL(v4l2_try_ext_ctrls); + +int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) +{ + return try_set_ext_ctrls(hdl, cs, true); +} +EXPORT_SYMBOL(v4l2_s_ext_ctrls); + +int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs) +{ + return try_set_ext_ctrls(sd->ctrl_handler, cs, false); +} +EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls); + +int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs) +{ + return try_set_ext_ctrls(sd->ctrl_handler, cs, true); +} +EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls); + +/* Helper function for VIDIOC_S_CTRL compatibility */ +static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val) +{ + struct v4l2_ctrl *master = ctrl->cluster[0]; + int ret; + int i; + + v4l2_ctrl_lock(ctrl); + + /* Reset the 'has_new' flags of the cluster */ + for (i = 0; i < master->ncontrols; i++) + if (master->cluster[i]) + master->cluster[i]->has_new = 0; + + ctrl->val = *val; + ctrl->has_new = 1; + ret = try_or_set_control_cluster(master, false); + if (!ret) + ret = try_or_set_control_cluster(master, true); + *val = ctrl->cur.val; + v4l2_ctrl_unlock(ctrl); + return ret; +} + +int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control) +{ + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id); + + if (ctrl == NULL || !type_is_int(ctrl)) + return -EINVAL; + + return set_ctrl(ctrl, &control->value); +} +EXPORT_SYMBOL(v4l2_s_ctrl); + +int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) +{ + return v4l2_s_ctrl(sd->ctrl_handler, control); +} +EXPORT_SYMBOL(v4l2_subdev_s_ctrl); + +int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +{ + /* It's a driver bug if this happens. */ + WARN_ON(!type_is_int(ctrl)); + return set_ctrl(ctrl, &val); +} +EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 0ca7ec9ca90..cb77197d480 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -25,6 +25,7 @@ #include <linux/init.h> #include <linux/kmod.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -215,28 +216,24 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) return vdev->fops->poll(filp, poll); } -static int v4l2_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vdev = video_devdata(filp); + int ret; - if (!vdev->fops->ioctl) - return -ENOTTY; /* Allow ioctl to continue even if the device was unregistered. Things like dequeueing buffers might still be useful. */ - return vdev->fops->ioctl(filp, cmd, arg); -} - -static long v4l2_unlocked_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct video_device *vdev = video_devdata(filp); + if (vdev->fops->unlocked_ioctl) { + ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); + } else if (vdev->fops->ioctl) { + /* TODO: convert all drivers to unlocked_ioctl */ + lock_kernel(); + ret = vdev->fops->ioctl(filp, cmd, arg); + unlock_kernel(); + } else + ret = -ENOTTY; - if (!vdev->fops->unlocked_ioctl) - return -ENOTTY; - /* Allow ioctl to continue even if the device was unregistered. - Things like dequeueing buffers might still be useful. */ - return vdev->fops->unlocked_ioctl(filp, cmd, arg); + return ret; } #ifdef CONFIG_MMU @@ -307,22 +304,6 @@ static int v4l2_release(struct inode *inode, struct file *filp) return ret; } -static const struct file_operations v4l2_unlocked_fops = { - .owner = THIS_MODULE, - .read = v4l2_read, - .write = v4l2_write, - .open = v4l2_open, - .get_unmapped_area = v4l2_get_unmapped_area, - .mmap = v4l2_mmap, - .unlocked_ioctl = v4l2_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = v4l2_compat_ioctl32, -#endif - .release = v4l2_release, - .poll = v4l2_poll, - .llseek = no_llseek, -}; - static const struct file_operations v4l2_fops = { .owner = THIS_MODULE, .read = v4l2_read, @@ -330,7 +311,7 @@ static const struct file_operations v4l2_fops = { .open = v4l2_open, .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, - .ioctl = v4l2_ioctl, + .unlocked_ioctl = v4l2_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l2_compat_ioctl32, #endif @@ -410,7 +391,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; const char *name_base; - void *priv = video_get_drvdata(vdev); + void *priv = vdev->dev.p; /* A minor value of -1 marks this video device as never having been registered */ @@ -447,8 +428,12 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; - if (vdev->v4l2_dev && vdev->v4l2_dev->dev) - vdev->parent = vdev->v4l2_dev->dev; + if (vdev->v4l2_dev) { + if (vdev->v4l2_dev->dev) + vdev->parent = vdev->v4l2_dev->dev; + if (vdev->ctrl_handler == NULL) + vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; + } /* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES @@ -521,10 +506,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, ret = -ENOMEM; goto cleanup; } - if (vdev->fops->unlocked_ioctl) - vdev->cdev->ops = &v4l2_unlocked_fops; - else - vdev->cdev->ops = &v4l2_fops; + vdev->cdev->ops = &v4l2_fops; vdev->cdev->owner = vdev->fops->owner; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { @@ -536,9 +518,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, /* Part 4: register the device with sysfs */ memset(&vdev->dev, 0, sizeof(vdev->dev)); - /* The memset above cleared the device's drvdata, so + /* The memset above cleared the device's device_private, so put back the copy we made earlier. */ - video_set_drvdata(vdev, priv); + vdev->dev.p = priv; vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); if (vdev->parent) diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 5a7dc4afe92..0b08f96b74a 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -26,6 +26,7 @@ #endif #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) { @@ -115,6 +116,8 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister); int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd) { + int err; + /* Check for valid input */ if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) return -EINVAL; @@ -122,6 +125,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, WARN_ON(sd->v4l2_dev != NULL); if (!try_module_get(sd->owner)) return -ENODEV; + /* This just returns 0 if either of the two args is NULL */ + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); + if (err) + return err; sd->v4l2_dev = v4l2_dev; spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 0eeceae5032..dd9283fcb56 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -26,6 +26,7 @@ #endif #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-fh.h> #include <media/v4l2-event.h> #include <media/v4l2-chip-ident.h> @@ -1259,9 +1260,12 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_queryctrl *p = arg; - if (!ops->vidioc_queryctrl) + if (vfd->ctrl_handler) + ret = v4l2_queryctrl(vfd->ctrl_handler, p); + else if (ops->vidioc_queryctrl) + ret = ops->vidioc_queryctrl(file, fh, p); + else break; - ret = ops->vidioc_queryctrl(file, fh, p); if (!ret) dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, " "step=%d, default=%d, flags=0x%08x\n", @@ -1276,7 +1280,9 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_control *p = arg; - if (ops->vidioc_g_ctrl) + if (vfd->ctrl_handler) + ret = v4l2_g_ctrl(vfd->ctrl_handler, p); + else if (ops->vidioc_g_ctrl) ret = ops->vidioc_g_ctrl(file, fh, p); else if (ops->vidioc_g_ext_ctrls) { struct v4l2_ext_controls ctrls; @@ -1306,11 +1312,16 @@ static long __video_do_ioctl(struct file *file, struct v4l2_ext_controls ctrls; struct v4l2_ext_control ctrl; - if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) + if (!vfd->ctrl_handler && + !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) break; dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); + if (vfd->ctrl_handler) { + ret = v4l2_s_ctrl(vfd->ctrl_handler, p); + break; + } if (ops->vidioc_s_ctrl) { ret = ops->vidioc_s_ctrl(file, fh, p); break; @@ -1332,10 +1343,12 @@ static long __video_do_ioctl(struct file *file, struct v4l2_ext_controls *p = arg; p->error_idx = p->count; - if (!ops->vidioc_g_ext_ctrls) - break; - if (check_ext_ctrls(p, 0)) + if (vfd->ctrl_handler) + ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p); + else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0)) ret = ops->vidioc_g_ext_ctrls(file, fh, p); + else + break; v4l_print_ext_ctrls(cmd, vfd, p, !ret); break; } @@ -1344,10 +1357,12 @@ static long __video_do_ioctl(struct file *file, struct v4l2_ext_controls *p = arg; p->error_idx = p->count; - if (!ops->vidioc_s_ext_ctrls) + if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls) break; v4l_print_ext_ctrls(cmd, vfd, p, 1); - if (check_ext_ctrls(p, 0)) + if (vfd->ctrl_handler) + ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p); + else if (check_ext_ctrls(p, 0)) ret = ops->vidioc_s_ext_ctrls(file, fh, p); break; } @@ -1356,10 +1371,12 @@ static long __video_do_ioctl(struct file *file, struct v4l2_ext_controls *p = arg; p->error_idx = p->count; - if (!ops->vidioc_try_ext_ctrls) + if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls) break; v4l_print_ext_ctrls(cmd, vfd, p, 1); - if (check_ext_ctrls(p, 0)) + if (vfd->ctrl_handler) + ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p); + else if (check_ext_ctrls(p, 0)) ret = ops->vidioc_try_ext_ctrls(file, fh, p); break; } @@ -1367,9 +1384,12 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_querymenu *p = arg; - if (!ops->vidioc_querymenu) + if (vfd->ctrl_handler) + ret = v4l2_querymenu(vfd->ctrl_handler, p); + else if (ops->vidioc_querymenu) + ret = ops->vidioc_querymenu(file, fh, p); + else break; - ret = ops->vidioc_querymenu(file, fh, p); if (!ret) dbgarg(cmd, "id=0x%x, index=%d, name=%s\n", p->id, p->index, p->name); diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 7d3378437de..ce1595bef62 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -52,18 +52,18 @@ MODULE_LICENSE("GPL"); #define CALL(q, f, arg...) \ ((q->int_ops->f) ? q->int_ops->f(arg) : 0) -struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) +struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q) { struct videobuf_buffer *vb; BUG_ON(q->msize < sizeof(*vb)); - if (!q->int_ops || !q->int_ops->alloc) { + if (!q->int_ops || !q->int_ops->alloc_vb) { printk(KERN_ERR "No specific ops defined!\n"); BUG(); } - vb = q->int_ops->alloc(q->msize); + vb = q->int_ops->alloc_vb(q->msize); if (NULL != vb) { init_waitqueue_head(&vb->done); vb->magic = MAGIC_BUFFER; @@ -71,7 +71,7 @@ struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) return vb; } -EXPORT_SYMBOL_GPL(videobuf_alloc); +EXPORT_SYMBOL_GPL(videobuf_alloc_vb); #define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\ vb->state != VIDEOBUF_QUEUED) @@ -195,6 +195,45 @@ int videobuf_queue_is_busy(struct videobuf_queue *q) } EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); +/** + * __videobuf_free() - free all the buffers and their control structures + * + * This function can only be called if streaming/reading is off, i.e. no buffers + * are under control of the driver. + */ +/* Locking: Caller holds q->vb_lock */ +static int __videobuf_free(struct videobuf_queue *q) +{ + int i; + + dprintk(1, "%s\n", __func__); + if (!q) + return 0; + + if (q->streaming || q->reading) { + dprintk(1, "Cannot free buffers when streaming or reading\n"); + return -EBUSY; + } + + MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) + if (q->bufs[i] && q->bufs[i]->map) { + dprintk(1, "Cannot free mmapped buffers\n"); + return -EBUSY; + } + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + q->ops->buf_release(q, q->bufs[i]); + kfree(q->bufs[i]); + q->bufs[i] = NULL; + } + + return 0; +} + /* Locking: Caller holds q->vb_lock */ void videobuf_queue_cancel(struct videobuf_queue *q) { @@ -308,36 +347,11 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, b->sequence = vb->field_count >> 1; } -/* Locking: Caller holds q->vb_lock */ -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - int i; - - if (!q) - return 0; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - for (i = 0; i < VIDEO_MAX_FRAME; i++) - if (q->bufs[i] && q->bufs[i]->map) - return -EBUSY; - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - q->ops->buf_release(q, q->bufs[i]); - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } - - return 0; -} - int videobuf_mmap_free(struct videobuf_queue *q) { int ret; mutex_lock(&q->vb_lock); - ret = __videobuf_mmap_free(q); + ret = __videobuf_free(q); mutex_unlock(&q->vb_lock); return ret; } @@ -353,13 +367,13 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - err = __videobuf_mmap_free(q); + err = __videobuf_free(q); if (0 != err) return err; /* Allocate and initialize buffers */ for (i = 0; i < bcount; i++) { - q->bufs[i] = videobuf_alloc(q); + q->bufs[i] = videobuf_alloc_vb(q); if (NULL == q->bufs[i]) break; @@ -766,7 +780,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); /* setup stuff */ - q->read_buf = videobuf_alloc(q); + q->read_buf = videobuf_alloc_vb(q); if (NULL == q->read_buf) return -ENOMEM; @@ -871,7 +885,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, if (NULL == q->read_buf) { /* need to capture a new frame */ retval = -ENOMEM; - q->read_buf = videobuf_alloc(q); + q->read_buf = videobuf_alloc_vb(q); dprintk(1, "video alloc=0x%p\n", q->read_buf); if (NULL == q->read_buf) @@ -970,7 +984,7 @@ static void __videobuf_read_stop(struct videobuf_queue *q) int i; videobuf_queue_cancel(q); - __videobuf_mmap_free(q); + __videobuf_free(q); INIT_LIST_HEAD(&q->stream); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index 74730c624cf..372b87efcd0 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -190,7 +190,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, return ret; } -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -280,8 +280,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, return -ENOMEM; buf->map = map; - map->start = vma->vm_start; - map->end = vma->vm_end; map->q = q; buf->baddr = vma->vm_start; @@ -338,7 +336,7 @@ error: static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_vb, .iolock = __videobuf_iolock, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = __videobuf_to_vaddr, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 8359e6badd3..06f9a9c2a39 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -57,7 +57,13 @@ MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ -struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) +/* + * Return a scatterlist for some page-aligned vmalloc()'ed memory + * block (NULL on errors). Memory for the scatterlist is allocated + * using kmalloc. The caller must free the memory. + */ +static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, + int nr_pages) { struct scatterlist *sglist; struct page *pg; @@ -81,10 +87,14 @@ err: vfree(sglist); return NULL; } -EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); -struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, - int offset) +/* + * Return a scatterlist for a an array of userpages (NULL on errors). + * Memory for the scatterlist is allocated using kmalloc. The caller + * must free the memory. + */ +static struct scatterlist *videobuf_pages_to_sg(struct page **pages, + int nr_pages, int offset) { struct scatterlist *sglist; int i; @@ -201,17 +211,17 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dprintk(1, "init kernel [%d pages]\n", nr_pages); dma->direction = direction; - dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT); - if (NULL == dma->vmalloc) { + dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == dma->vaddr) { dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); return -ENOMEM; } dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)dma->vmalloc, + (unsigned long)dma->vaddr, nr_pages << PAGE_SHIFT); - memset(dma->vmalloc, 0, nr_pages << PAGE_SHIFT); + memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; return 0; @@ -235,7 +245,7 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, } EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) +int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); @@ -244,8 +254,8 @@ int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, dma->offset); } - if (dma->vmalloc) { - dma->sglist = videobuf_vmalloc_to_sg(dma->vmalloc, + if (dma->vaddr) { + dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr, dma->nr_pages); } if (dma->bus_addr) { @@ -263,7 +273,7 @@ int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) return -ENOMEM; } if (!dma->bus_addr) { - dma->sglen = dma_map_sg(q->dev, dma->sglist, + dma->sglen = dma_map_sg(dev, dma->sglist, dma->nr_pages, dma->direction); if (0 == dma->sglen) { printk(KERN_WARNING @@ -279,14 +289,14 @@ int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) } EXPORT_SYMBOL_GPL(videobuf_dma_map); -int videobuf_dma_unmap(struct videobuf_queue *q, struct videobuf_dmabuf *dma) +int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); if (!dma->sglen) return 0; - dma_unmap_sg(q->dev, dma->sglist, dma->sglen, dma->direction); + dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction); vfree(dma->sglist); dma->sglist = NULL; @@ -309,8 +319,8 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) dma->pages = NULL; } - vfree(dma->vmalloc); - dma->vmalloc = NULL; + vfree(dma->vaddr); + dma->vaddr = NULL; if (dma->bus_addr) dma->bus_addr = 0; @@ -322,28 +332,6 @@ EXPORT_SYMBOL_GPL(videobuf_dma_free); /* --------------------------------------------------------------------- */ -int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma) -{ - struct videobuf_queue q; - - q.dev = dev; - - return videobuf_dma_map(&q, dma); -} -EXPORT_SYMBOL_GPL(videobuf_sg_dma_map); - -int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) -{ - struct videobuf_queue q; - - q.dev = dev; - - return videobuf_dma_unmap(&q, dma); -} -EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); - -/* --------------------------------------------------------------------- */ - static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; @@ -428,7 +416,7 @@ static const struct vm_operations_struct videobuf_vm_ops = { struct videobuf_dma_sg_memory */ -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) { struct videobuf_dma_sg_memory *mem; struct videobuf_buffer *vb; @@ -456,7 +444,7 @@ static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - return mem->dma.vmalloc; + return mem->dma.vaddr; } static int __videobuf_iolock(struct videobuf_queue *q, @@ -520,7 +508,7 @@ static int __videobuf_iolock(struct videobuf_queue *q, default: BUG(); } - err = videobuf_dma_map(q, &mem->dma); + err = videobuf_dma_map(q->dev, &mem->dma); if (0 != err) return err; @@ -620,8 +608,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, } map->count = 1; - map->start = vma->vm_start; - map->end = vma->vm_end; map->q = q; vma->vm_ops = &videobuf_vm_ops; vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; @@ -638,7 +624,7 @@ done: static struct videobuf_qtype_ops sg_ops = { .magic = MAGIC_QTYPE_OPS, - .alloc = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_vb, .iolock = __videobuf_iolock, .sync = __videobuf_sync, .mmap_mapper = __videobuf_mmap_mapper, @@ -654,7 +640,7 @@ void *videobuf_sg_alloc(size_t size) q.msize = size; - return videobuf_alloc(&q); + return videobuf_alloc_vb(&q); } EXPORT_SYMBOL_GPL(videobuf_sg_alloc); diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 583728f4c22..e7fe31d54f0 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -102,10 +102,10 @@ static void videobuf_vm_close(struct vm_area_struct *vma) called with IRQ's disabled */ dprintk(1, "%s: buf[%d] freeing (%p)\n", - __func__, i, mem->vmalloc); + __func__, i, mem->vaddr); - vfree(mem->vmalloc); - mem->vmalloc = NULL; + vfree(mem->vaddr); + mem->vaddr = NULL; } q->bufs[i]->map = NULL; @@ -135,7 +135,7 @@ static const struct vm_operations_struct videobuf_vm_ops = { struct videobuf_dma_sg_memory */ -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) { struct videobuf_vmalloc_memory *mem; struct videobuf_buffer *vb; @@ -170,7 +170,7 @@ static int __videobuf_iolock(struct videobuf_queue *q, dprintk(1, "%s memory method MMAP\n", __func__); /* All handling should be done by __videobuf_mmap_mapper() */ - if (!mem->vmalloc) { + if (!mem->vaddr) { printk(KERN_ERR "memory is not alloced/mmapped.\n"); return -EINVAL; } @@ -189,13 +189,13 @@ static int __videobuf_iolock(struct videobuf_queue *q, * read() method. */ - mem->vmalloc = vmalloc_user(pages); - if (!mem->vmalloc) { + mem->vaddr = vmalloc_user(pages); + if (!mem->vaddr) { printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); return -ENOMEM; } dprintk(1, "vmalloc is at addr %p (%d pages)\n", - mem->vmalloc, pages); + mem->vaddr, pages); #if 0 int rc; @@ -245,8 +245,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, return -ENOMEM; buf->map = map; - map->start = vma->vm_start; - map->end = vma->vm_end; map->q = q; buf->baddr = vma->vm_start; @@ -256,18 +254,18 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); - mem->vmalloc = vmalloc_user(pages); - if (!mem->vmalloc) { + mem->vaddr = vmalloc_user(pages); + if (!mem->vaddr) { printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); goto error; } - dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vmalloc, pages); + dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages); /* Try to remap memory */ - retval = remap_vmalloc_range(vma, mem->vmalloc, 0); + retval = remap_vmalloc_range(vma, mem->vaddr, 0); if (retval < 0) { printk(KERN_ERR "mmap: remap failed with error %d. ", retval); - vfree(mem->vmalloc); + vfree(mem->vaddr); goto error; } @@ -293,7 +291,7 @@ error: static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_vb, .iolock = __videobuf_iolock, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = videobuf_to_vmalloc, @@ -319,7 +317,7 @@ void *videobuf_to_vmalloc(struct videobuf_buffer *buf) BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - return mem->vmalloc; + return mem->vaddr; } EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); @@ -341,8 +339,8 @@ void videobuf_vmalloc_free(struct videobuf_buffer *buf) MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - vfree(mem->vmalloc); - mem->vmalloc = NULL; + vfree(mem->vaddr); + mem->vaddr = NULL; return; } diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c deleted file mode 100644 index d807eea9175..00000000000 --- a/drivers/media/video/w9968cf.c +++ /dev/null @@ -1,3620 +0,0 @@ -/*************************************************************************** - * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * - * * - * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * - Memory management code from bttv driver by Ralph Metzler, * - * Marcus Metzler and Gerd Knorr. * - * - I2C interface to kernel, high-level image sensor control routines and * - * some symbolic names from OV511 driver by Mark W. McClelland. * - * - Low-level I2C fast write function by Piotr Czerczak. * - * - Low-level I2C read function by Frederic Jouault. * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/kmod.h> -#include <linux/init.h> -#include <linux/fs.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/ioctl.h> -#include <linux/delay.h> -#include <linux/stddef.h> -#include <asm/page.h> -#include <asm/uaccess.h> -#include <linux/page-flags.h> -#include <linux/videodev.h> -#include <media/v4l2-ioctl.h> - -#include "w9968cf.h" -#include "w9968cf_decoder.h" - -static struct w9968cf_vpp_t* w9968cf_vpp; -static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait); - -static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */ -static DEFINE_MUTEX(w9968cf_devlist_mutex); /* semaphore for list traversal */ - -static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */ - - -/**************************************************************************** - * Module macros and parameters * - ****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, winbond_id_table); - -MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL); -MODULE_DESCRIPTION(W9968CF_MODULE_NAME); -MODULE_VERSION(W9968CF_MODULE_VERSION); -MODULE_LICENSE(W9968CF_MODULE_LICENSE); -MODULE_SUPPORTED_DEVICE("Video"); - -static unsigned short simcams = W9968CF_SIMCAMS; -static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ -static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_PACKET_SIZE}; -static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_BUFFERS}; -static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_DOUBLE_BUFFER}; -static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; -static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_FILTER_TYPE}; -static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; -static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_DECOMPRESSION}; -static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; -static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; -static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; -static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; -static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; -static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_LIGHTFREQ}; -static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= - W9968CF_BANDINGFILTER}; -static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; -static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; -static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; -static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME}; -static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_BRIGHTNESS}; -static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; -static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; -static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_CONTRAST}; -static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = - W9968CF_WHITENESS}; -#ifdef W9968CF_DEBUG -static unsigned short debug = W9968CF_DEBUG_LEVEL; -static int specific_debug = W9968CF_SPECIFIC_DEBUG; -#endif - -static unsigned int param_nv[24]; /* number of values per parameter */ - -module_param(simcams, ushort, 0644); -module_param_array(video_nr, short, ¶m_nv[0], 0444); -module_param_array(packet_size, uint, ¶m_nv[1], 0444); -module_param_array(max_buffers, ushort, ¶m_nv[2], 0444); -module_param_array(double_buffer, bool, ¶m_nv[3], 0444); -module_param_array(clamping, bool, ¶m_nv[4], 0444); -module_param_array(filter_type, ushort, ¶m_nv[5], 0444); -module_param_array(largeview, bool, ¶m_nv[6], 0444); -module_param_array(decompression, ushort, ¶m_nv[7], 0444); -module_param_array(upscaling, bool, ¶m_nv[8], 0444); -module_param_array(force_palette, ushort, ¶m_nv[9], 0444); -module_param_array(force_rgb, ushort, ¶m_nv[10], 0444); -module_param_array(autobright, bool, ¶m_nv[11], 0444); -module_param_array(autoexp, bool, ¶m_nv[12], 0444); -module_param_array(lightfreq, ushort, ¶m_nv[13], 0444); -module_param_array(bandingfilter, bool, ¶m_nv[14], 0444); -module_param_array(clockdiv, short, ¶m_nv[15], 0444); -module_param_array(backlight, bool, ¶m_nv[16], 0444); -module_param_array(mirror, bool, ¶m_nv[17], 0444); -module_param_array(monochrome, bool, ¶m_nv[18], 0444); -module_param_array(brightness, uint, ¶m_nv[19], 0444); -module_param_array(hue, uint, ¶m_nv[20], 0444); -module_param_array(colour, uint, ¶m_nv[21], 0444); -module_param_array(contrast, uint, ¶m_nv[22], 0444); -module_param_array(whiteness, uint, ¶m_nv[23], 0444); -#ifdef W9968CF_DEBUG -module_param(debug, ushort, 0644); -module_param(specific_debug, bool, 0644); -#endif - -MODULE_PARM_DESC(simcams, - "\n<n> Number of cameras allowed to stream simultaneously." - "\nn may vary from 0 to " - __MODULE_STRING(W9968CF_MAX_DEVICES)"." - "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"." - "\n"); -MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES) - " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second camera and use auto for the first" - "\none and for every other camera." - "\n"); -MODULE_PARM_DESC(packet_size, - "\n<n[,...]> Specify the maximum data payload" - "\nsize in bytes for alternate settings, for each device." - "\nn is scaled between 63 and 1023 " - "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")." - "\n"); -MODULE_PARM_DESC(max_buffers, - "\n<n[,...]> For advanced users." - "\nSpecify the maximum number of video frame buffers" - "\nto allocate for each device, from 2 to " - __MODULE_STRING(W9968CF_MAX_BUFFERS) - ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")." - "\n"); -MODULE_PARM_DESC(double_buffer, - "\n<0|1[,...]> " - "Hardware double buffering: 0 disabled, 1 enabled." - "\nIt should be enabled if you want smooth video output: if" - "\nyou obtain out of sync. video, disable it, or try to" - "\ndecrease the 'clockdiv' module parameter value." - "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) - " for every device." - "\n"); -MODULE_PARM_DESC(clamping, - "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled." - "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING) - " for every device." - "\n"); -MODULE_PARM_DESC(filter_type, - "\n<0|1|2[,...]> Video filter type." - "\n0 none, 1 (1-2-1) 3-tap filter, " - "2 (2-3-6-3-2) 5-tap filter." - "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE) - " for every device." - "\nThe filter is used to reduce noise and aliasing artifacts" - "\nproduced by the CCD or CMOS image sensor, and the scaling" - " process." - "\n"); -MODULE_PARM_DESC(largeview, - "\n<0|1[,...]> Large view: 0 disabled, 1 enabled." - "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW) - " for every device." - "\n"); -MODULE_PARM_DESC(upscaling, - "\n<0|1[,...]> Software scaling (for non-compressed video):" - "\n0 disabled, 1 enabled." - "\nDisable it if you have a slow CPU or you don't have" - " enough memory." - "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) - " for every device." - "\nIf 'w9968cf-vpp' is not present, this parameter is" - " set to 0." - "\n"); -MODULE_PARM_DESC(decompression, - "\n<0|1|2[,...]> Software video decompression:" - "\n- 0 disables decompression (doesn't allow formats needing" - " decompression)" - "\n- 1 forces decompression (allows formats needing" - " decompression only);" - "\n- 2 allows any permitted formats." - "\nFormats supporting compressed video are YUV422P and" - " YUV420P/YUV420 " - "\nin any resolutions where both width and height are " - "a multiple of 16." - "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION) - " for every device." - "\nIf 'w9968cf-vpp' is not present, forcing decompression is " - "\nnot allowed; in this case this parameter is set to 2." - "\n"); -MODULE_PARM_DESC(force_palette, - "\n<0" - "|" __MODULE_STRING(VIDEO_PALETTE_UYVY) - "|" __MODULE_STRING(VIDEO_PALETTE_YUV420) - "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P) - "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P) - "|" __MODULE_STRING(VIDEO_PALETTE_YUYV) - "|" __MODULE_STRING(VIDEO_PALETTE_YUV422) - "|" __MODULE_STRING(VIDEO_PALETTE_GREY) - "|" __MODULE_STRING(VIDEO_PALETTE_RGB555) - "|" __MODULE_STRING(VIDEO_PALETTE_RGB565) - "|" __MODULE_STRING(VIDEO_PALETTE_RGB24) - "|" __MODULE_STRING(VIDEO_PALETTE_RGB32) - "[,...]>" - " Force picture palette." - "\nIn order:" - "\n- 0 allows any of the following formats:" - "\n- UYVY 16 bpp - Original video, compression disabled" - "\n- YUV420 12 bpp - Original video, compression enabled" - "\n- YUV422P 16 bpp - Original video, compression enabled" - "\n- YUV420P 12 bpp - Original video, compression enabled" - "\n- YUVY 16 bpp - Software conversion from UYVY" - "\n- YUV422 16 bpp - Software conversion from UYVY" - "\n- GREY 8 bpp - Software conversion from UYVY" - "\n- RGB555 16 bpp - Software conversion from UYVY" - "\n- RGB565 16 bpp - Software conversion from UYVY" - "\n- RGB24 24 bpp - Software conversion from UYVY" - "\n- RGB32 32 bpp - Software conversion from UYVY" - "\nWhen not 0, this parameter will override 'decompression'." - "\nDefault value is 0 for every device." - "\nInitial palette is " - __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." - "\nIf 'w9968cf-vpp' is not present, this parameter is" - " set to 9 (UYVY)." - "\n"); -MODULE_PARM_DESC(force_rgb, - "\n<0|1[,...]> Read RGB video data instead of BGR:" - "\n 1 = use RGB component ordering." - "\n 0 = use BGR component ordering." - "\nThis parameter has effect when using RGBX palettes only." - "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB) - " for every device." - "\n"); -MODULE_PARM_DESC(autobright, - "\n<0|1[,...]> Image sensor automatically changes brightness:" - "\n 0 = no, 1 = yes" - "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT) - " for every device." - "\n"); -MODULE_PARM_DESC(autoexp, - "\n<0|1[,...]> Image sensor automatically changes exposure:" - "\n 0 = no, 1 = yes" - "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) - " for every device." - "\n"); -MODULE_PARM_DESC(lightfreq, - "\n<50|60[,...]> Light frequency in Hz:" - "\n 50 for European and Asian lighting," - " 60 for American lighting." - "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ) - " for every device." - "\n"); -MODULE_PARM_DESC(bandingfilter, - "\n<0|1[,...]> Banding filter to reduce effects of" - " fluorescent lighting:" - "\n 0 disabled, 1 enabled." - "\nThis filter tries to reduce the pattern of horizontal" - "\nlight/dark bands caused by some (usually fluorescent)" - " lighting." - "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER) - " for every device." - "\n"); -MODULE_PARM_DESC(clockdiv, - "\n<-1|n[,...]> " - "Force pixel clock divisor to a specific value (for experts):" - "\n n may vary from 0 to 127." - "\n -1 for automatic value." - "\nSee also the 'double_buffer' module parameter." - "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV) - " for every device." - "\n"); -MODULE_PARM_DESC(backlight, - "\n<0|1[,...]> Objects are lit from behind:" - "\n 0 = no, 1 = yes" - "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT) - " for every device." - "\n"); -MODULE_PARM_DESC(mirror, - "\n<0|1[,...]> Reverse image horizontally:" - "\n 0 = no, 1 = yes" - "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR) - " for every device." - "\n"); -MODULE_PARM_DESC(monochrome, - "\n<0|1[,...]> Use image sensor as monochrome sensor:" - "\n 0 = no, 1 = yes" - "\nNot all the sensors support monochrome color." - "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME) - " for every device." - "\n"); -MODULE_PARM_DESC(brightness, - "\n<n[,...]> Set picture brightness (0-65535)." - "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS) - " for every device." - "\nThis parameter has no effect if 'autobright' is enabled." - "\n"); -MODULE_PARM_DESC(hue, - "\n<n[,...]> Set picture hue (0-65535)." - "\nDefault value is "__MODULE_STRING(W9968CF_HUE) - " for every device." - "\n"); -MODULE_PARM_DESC(colour, - "\n<n[,...]> Set picture saturation (0-65535)." - "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR) - " for every device." - "\n"); -MODULE_PARM_DESC(contrast, - "\n<n[,...]> Set picture contrast (0-65535)." - "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST) - " for every device." - "\n"); -MODULE_PARM_DESC(whiteness, - "\n<n[,...]> Set picture whiteness (0-65535)." - "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS) - " for every device." - "\n"); -#ifdef W9968CF_DEBUG -MODULE_PARM_DESC(debug, - "\n<n> Debugging information level, from 0 to 6:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = configuration or general messages" - "\n4 = warnings" - "\n5 = called functions" - "\n6 = function internals" - "\nLevel 5 and 6 are useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." - "\n"); -MODULE_PARM_DESC(specific_debug, - "\n<0|1> Enable or disable specific debugging messages:" - "\n0 = print messages concerning every level" - " <= 'debug' level." - "\n1 = print messages concerning the level" - " indicated by 'debug'." - "\nDefault value is " - __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"." - "\n"); -#endif /* W9968CF_DEBUG */ - - - -/**************************************************************************** - * Some prototypes * - ****************************************************************************/ - -/* Video4linux interface */ -static const struct v4l2_file_operations w9968cf_fops; -static int w9968cf_open(struct file *); -static int w9968cf_release(struct file *); -static int w9968cf_mmap(struct file *, struct vm_area_struct *); -static long w9968cf_ioctl(struct file *, unsigned, unsigned long); -static ssize_t w9968cf_read(struct file *, char __user *, size_t, loff_t *); -static long w9968cf_v4l_ioctl(struct file *, unsigned int, - void __user *); - -/* USB-specific */ -static int w9968cf_start_transfer(struct w9968cf_device*); -static int w9968cf_stop_transfer(struct w9968cf_device*); -static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index); -static int w9968cf_read_reg(struct w9968cf_device*, u16 index); -static int w9968cf_write_fsb(struct w9968cf_device*, u16* data); -static int w9968cf_write_sb(struct w9968cf_device*, u16 value); -static int w9968cf_read_sb(struct w9968cf_device*); -static int w9968cf_upload_quantizationtables(struct w9968cf_device*); -static void w9968cf_urb_complete(struct urb *urb); - -/* Low-level I2C (SMBus) I/O */ -static int w9968cf_smbus_start(struct w9968cf_device*); -static int w9968cf_smbus_stop(struct w9968cf_device*); -static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v); -static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v); -static int w9968cf_smbus_write_ack(struct w9968cf_device*); -static int w9968cf_smbus_read_ack(struct w9968cf_device*); -static int w9968cf_smbus_refresh_bus(struct w9968cf_device*); -static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, - u16 address, u8* value); -static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, - u8 subaddress, u8* value); -static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*, - u16 address, u8 subaddress); -static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*, - u16 address, u8 subaddress, - u8 value); - -/* I2C interface to kernel */ -static int w9968cf_i2c_init(struct w9968cf_device*); -static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, - unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data*); -static u32 w9968cf_i2c_func(struct i2c_adapter*); - -/* Memory management */ -static void* rvmalloc(unsigned long size); -static void rvfree(void *mem, unsigned long size); -static void w9968cf_deallocate_memory(struct w9968cf_device*); -static int w9968cf_allocate_memory(struct w9968cf_device*); - -/* High-level image sensor control functions */ -static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val); -static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val); -static int w9968cf_sensor_cmd(struct w9968cf_device*, - unsigned int cmd, void *arg); -static int w9968cf_sensor_init(struct w9968cf_device*); -static int w9968cf_sensor_update_settings(struct w9968cf_device*); -static int w9968cf_sensor_get_picture(struct w9968cf_device*); -static int w9968cf_sensor_update_picture(struct w9968cf_device*, - struct video_picture pict); - -/* Other helper functions */ -static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, - enum w9968cf_model_id, - const unsigned short dev_nr); -static void w9968cf_adjust_configuration(struct w9968cf_device*); -static int w9968cf_turn_on_led(struct w9968cf_device*); -static int w9968cf_init_chip(struct w9968cf_device*); -static inline u16 w9968cf_valid_palette(u16 palette); -static inline u16 w9968cf_valid_depth(u16 palette); -static inline u8 w9968cf_need_decompression(u16 palette); -static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); -static int w9968cf_set_window(struct w9968cf_device*, struct video_window); -static int w9968cf_postprocess_frame(struct w9968cf_device*, - struct w9968cf_frame_t*); -static int w9968cf_adjust_window_size(struct w9968cf_device*, u32 *w, u32 *h); -static void w9968cf_init_framelist(struct w9968cf_device*); -static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); -static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); -static void w9968cf_release_resources(struct w9968cf_device*); - - - -/**************************************************************************** - * Symbolic names * - ****************************************************************************/ - -/* Used to represent a list of values and their respective symbolic names */ -struct w9968cf_symbolic_list { - const int num; - const char *name; -}; - -/*-------------------------------------------------------------------------- - Returns the name of the matching element in the symbolic_list array. The - end of the list must be marked with an element that has a NULL name. - --------------------------------------------------------------------------*/ -static inline const char * -symbolic(struct w9968cf_symbolic_list list[], const int num) -{ - int i; - - for (i = 0; list[i].name != NULL; i++) - if (list[i].num == num) - return (list[i].name); - - return "Unknown"; -} - -static struct w9968cf_symbolic_list camlist[] = { - { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" }, - { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" }, - - /* Other cameras (having the same descriptors as Generic W996[87]CF) */ - { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" }, - { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" }, - { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, - { W9968CF_MOD_LL, "Lebon LDC-035A" }, - { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" }, - { W9968CF_MOD_OOE, "OmniVision OV8610-EDE" }, - { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" }, - { W9968CF_MOD_PDPII, "Pretec Digi Pen-II" }, - { W9968CF_MOD_PDP480, "Pretec DigiPen-480" }, - - { -1, NULL } -}; - -static struct w9968cf_symbolic_list senlist[] = { - { CC_OV76BE, "OV76BE" }, - { CC_OV7610, "OV7610" }, - { CC_OV7620, "OV7620" }, - { CC_OV7620AE, "OV7620AE" }, - { CC_OV6620, "OV6620" }, - { CC_OV6630, "OV6630" }, - { CC_OV6630AE, "OV6630AE" }, - { CC_OV6630AF, "OV6630AF" }, - { -1, NULL } -}; - -/* Video4Linux1 palettes */ -static struct w9968cf_symbolic_list v4l1_plist[] = { - { VIDEO_PALETTE_GREY, "GREY" }, - { VIDEO_PALETTE_HI240, "HI240" }, - { VIDEO_PALETTE_RGB565, "RGB565" }, - { VIDEO_PALETTE_RGB24, "RGB24" }, - { VIDEO_PALETTE_RGB32, "RGB32" }, - { VIDEO_PALETTE_RGB555, "RGB555" }, - { VIDEO_PALETTE_YUV422, "YUV422" }, - { VIDEO_PALETTE_YUYV, "YUYV" }, - { VIDEO_PALETTE_UYVY, "UYVY" }, - { VIDEO_PALETTE_YUV420, "YUV420" }, - { VIDEO_PALETTE_YUV411, "YUV411" }, - { VIDEO_PALETTE_RAW, "RAW" }, - { VIDEO_PALETTE_YUV422P, "YUV422P" }, - { VIDEO_PALETTE_YUV411P, "YUV411P" }, - { VIDEO_PALETTE_YUV420P, "YUV420P" }, - { VIDEO_PALETTE_YUV410P, "YUV410P" }, - { -1, NULL } -}; - -/* Decoder error codes: */ -static struct w9968cf_symbolic_list decoder_errlist[] = { - { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" }, - { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" }, - { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" }, - { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" }, - { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" }, - { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" }, - { -1, NULL } -}; - -/* URB error codes: */ -static struct w9968cf_symbolic_list urb_errlist[] = { - { -ENOMEM, "No memory for allocation of internal structures" }, - { -ENOSPC, "The host controller's bandwidth is already consumed" }, - { -ENOENT, "URB was canceled by unlink_urb" }, - { -EXDEV, "ISO transfer only partially completed" }, - { -EAGAIN, "Too match scheduled for the future" }, - { -ENXIO, "URB already queued" }, - { -EFBIG, "Too much ISO frames requested" }, - { -ENOSR, "Buffer error (overrun)" }, - { -EPIPE, "Specified endpoint is stalled (device not responding)"}, - { -EOVERFLOW, "Babble (too much data)" }, - { -EPROTO, "Bit-stuff error (bad cable?)" }, - { -EILSEQ, "CRC/Timeout" }, - { -ETIME, "Device does not respond to token" }, - { -ETIMEDOUT, "Device does not respond to command" }, - { -1, NULL } -}; - -/**************************************************************************** - * Memory management functions * - ****************************************************************************/ -static void* rvmalloc(unsigned long size) -{ - void* mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - - -static void rvfree(void* mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - - -/*-------------------------------------------------------------------------- - Deallocate previously allocated memory. - --------------------------------------------------------------------------*/ -static void w9968cf_deallocate_memory(struct w9968cf_device* cam) -{ - u8 i; - - /* Free the isochronous transfer buffers */ - for (i = 0; i < W9968CF_URBS; i++) { - kfree(cam->transfer_buffer[i]); - cam->transfer_buffer[i] = NULL; - } - - /* Free temporary frame buffer */ - if (cam->frame_tmp.buffer) { - rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size); - cam->frame_tmp.buffer = NULL; - } - - /* Free helper buffer */ - if (cam->frame_vpp.buffer) { - rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size); - cam->frame_vpp.buffer = NULL; - } - - /* Free video frame buffers */ - if (cam->frame[0].buffer) { - rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size); - cam->frame[0].buffer = NULL; - } - - cam->nbuffers = 0; - - DBG(5, "Memory successfully deallocated") -} - - -/*-------------------------------------------------------------------------- - Allocate memory buffers for USB transfers and video frames. - This function is called by open() only. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_allocate_memory(struct w9968cf_device* cam) -{ - const u16 p_size = wMaxPacketSize[cam->altsetting-1]; - void* buff = NULL; - unsigned long hw_bufsize, vpp_bufsize; - u8 i, bpp; - - /* NOTE: Deallocation is done elsewhere in case of error */ - - /* Calculate the max amount of raw data per frame from the device */ - hw_bufsize = cam->maxwidth*cam->maxheight*2; - - /* Calculate the max buf. size needed for post-processing routines */ - bpp = (w9968cf_vpp) ? 4 : 2; - if (cam->upscaling) - vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp, - cam->maxwidth*cam->maxheight*bpp); - else - vpp_bufsize = cam->maxwidth*cam->maxheight*bpp; - - /* Allocate memory for the isochronous transfer buffers */ - for (i = 0; i < W9968CF_URBS; i++) { - if (!(cam->transfer_buffer[i] = - kzalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { - DBG(1, "Couldn't allocate memory for the isochronous " - "transfer buffers (%u bytes)", - p_size * W9968CF_ISO_PACKETS) - return -ENOMEM; - } - } - - /* Allocate memory for the temporary frame buffer */ - if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) { - DBG(1, "Couldn't allocate memory for the temporary " - "video frame buffer (%lu bytes)", hw_bufsize) - return -ENOMEM; - } - cam->frame_tmp.size = hw_bufsize; - cam->frame_tmp.number = -1; - - /* Allocate memory for the helper buffer */ - if (w9968cf_vpp) { - if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) { - DBG(1, "Couldn't allocate memory for the helper buffer" - " (%lu bytes)", vpp_bufsize) - return -ENOMEM; - } - cam->frame_vpp.size = vpp_bufsize; - } else - cam->frame_vpp.buffer = NULL; - - /* Allocate memory for video frame buffers */ - cam->nbuffers = cam->max_buffers; - while (cam->nbuffers >= 2) { - if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize))) - break; - else - cam->nbuffers--; - } - - if (!buff) { - DBG(1, "Couldn't allocate memory for the video frame buffers") - cam->nbuffers = 0; - return -ENOMEM; - } - - if (cam->nbuffers != cam->max_buffers) - DBG(2, "Couldn't allocate memory for %u video frame buffers. " - "Only memory for %u buffers has been allocated", - cam->max_buffers, cam->nbuffers) - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].buffer = buff + i*vpp_bufsize; - cam->frame[i].size = vpp_bufsize; - cam->frame[i].number = i; - /* Circular list */ - if (i != cam->nbuffers-1) - cam->frame[i].next = &cam->frame[i+1]; - else - cam->frame[i].next = &cam->frame[0]; - cam->frame[i].status = F_UNUSED; - } - - DBG(5, "Memory successfully allocated") - return 0; -} - - - -/**************************************************************************** - * USB-specific functions * - ****************************************************************************/ - -/*-------------------------------------------------------------------------- - This is an handler function which is called after the URBs are completed. - It collects multiple data packets coming from the camera by putting them - into frame buffers: one or more zero data length data packets are used to - mark the end of a video frame; the first non-zero data packet is the start - of the next video frame; if an error is encountered in a packet, the entire - video frame is discarded and grabbed again. - If there are no requested frames in the FIFO list, packets are collected into - a temporary buffer. - --------------------------------------------------------------------------*/ -static void w9968cf_urb_complete(struct urb *urb) -{ - struct w9968cf_device* cam = (struct w9968cf_device*)urb->context; - struct w9968cf_frame_t** f; - unsigned int len, status; - void* pos; - u8 i; - int err = 0; - - if ((!cam->streaming) || cam->disconnected) { - DBG(4, "Got interrupt, but not streaming") - return; - } - - /* "(*f)" will be used instead of "cam->frame_current" */ - f = &cam->frame_current; - - /* If a frame has been requested and we are grabbing into - the temporary frame, we'll switch to that requested frame */ - if ((*f) == &cam->frame_tmp && *cam->requested_frame) { - if (cam->frame_tmp.status == F_GRABBING) { - w9968cf_pop_frame(cam, &cam->frame_current); - (*f)->status = F_GRABBING; - (*f)->length = cam->frame_tmp.length; - memcpy((*f)->buffer, cam->frame_tmp.buffer, - (*f)->length); - DBG(6, "Switched from temp. frame to frame #%d", - (*f)->number) - } - } - - for (i = 0; i < urb->number_of_packets; i++) { - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status && len != 0) { - DBG(4, "URB failed, error in data packet " - "(error #%u, %s)", - status, symbolic(urb_errlist, status)) - (*f)->status = F_ERROR; - continue; - } - - if (len) { /* start of frame */ - - if ((*f)->status == F_UNUSED) { - (*f)->status = F_GRABBING; - (*f)->length = 0; - } - - /* Buffer overflows shouldn't happen, however...*/ - if ((*f)->length + len > (*f)->size) { - DBG(4, "Buffer overflow: bad data packets") - (*f)->status = F_ERROR; - } - - if ((*f)->status == F_GRABBING) { - memcpy((*f)->buffer + (*f)->length, pos, len); - (*f)->length += len; - } - - } else if ((*f)->status == F_GRABBING) { /* end of frame */ - - DBG(6, "Frame #%d successfully grabbed", (*f)->number) - - if (cam->vpp_flag & VPP_DECOMPRESSION) { - err = w9968cf_vpp->check_headers((*f)->buffer, - (*f)->length); - if (err) { - DBG(4, "Skip corrupted frame: %s", - symbolic(decoder_errlist, err)) - (*f)->status = F_UNUSED; - continue; /* grab this frame again */ - } - } - - (*f)->status = F_READY; - (*f)->queued = 0; - - /* Take a pointer to the new frame from the FIFO list. - If the list is empty,we'll use the temporary frame*/ - if (*cam->requested_frame) - w9968cf_pop_frame(cam, &cam->frame_current); - else { - cam->frame_current = &cam->frame_tmp; - (*f)->status = F_UNUSED; - } - - } else if ((*f)->status == F_ERROR) - (*f)->status = F_UNUSED; /* grab it again */ - - PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d", - (unsigned long)(*f)->length, i, len, (*f)->status) - - } /* end for */ - - /* Resubmit this URB */ - urb->dev = cam->usbdev; - urb->status = 0; - spin_lock(&cam->urb_lock); - if (cam->streaming) - if ((err = usb_submit_urb(urb, GFP_ATOMIC))) { - cam->misconfigured = 1; - DBG(1, "Couldn't resubmit the URB: error %d, %s", - err, symbolic(urb_errlist, err)) - } - spin_unlock(&cam->urb_lock); - - /* Wake up the user process */ - wake_up_interruptible(&cam->wait_queue); -} - - -/*--------------------------------------------------------------------------- - Setup the URB structures for the isochronous transfer. - Submit the URBs so that the data transfer begins. - Return 0 on success, a negative number otherwise. - ---------------------------------------------------------------------------*/ -static int w9968cf_start_transfer(struct w9968cf_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb* urb; - const u16 p_size = wMaxPacketSize[cam->altsetting-1]; - u16 w, h, d; - int vidcapt; - u32 t_size; - int err = 0; - s8 i, j; - - for (i = 0; i < W9968CF_URBS; i++) { - urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); - if (!urb) { - for (j = 0; j < i; j++) - usb_free_urb(cam->urb[j]); - DBG(1, "Couldn't allocate the URB structures") - return -ENOMEM; - } - - cam->urb[i] = urb; - urb->dev = udev; - urb->context = (void*)cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = W9968CF_ISO_PACKETS; - urb->complete = w9968cf_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < W9968CF_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = p_size*j; - urb->iso_frame_desc[j].length = p_size; - } - } - - /* Transfer size per frame, in WORD ! */ - d = cam->hw_depth; - w = cam->hw_width; - h = cam->hw_height; - - t_size = (w*h*d)/16; - - err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ - err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ - - /* Transfer size */ - err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */ - err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */ - - if (cam->vpp_flag & VPP_DECOMPRESSION) - err += w9968cf_upload_quantizationtables(cam); - - vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */ - err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */ - - err += usb_set_interface(udev, 0, cam->altsetting); - err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */ - - if (err || (vidcapt < 0)) { - for (i = 0; i < W9968CF_URBS; i++) - usb_free_urb(cam->urb[i]); - DBG(1, "Couldn't tell the camera to start the data transfer") - return err; - } - - w9968cf_init_framelist(cam); - - /* Begin to grab into the temporary buffer */ - cam->frame_tmp.status = F_UNUSED; - cam->frame_tmp.queued = 0; - cam->frame_current = &cam->frame_tmp; - - if (!(cam->vpp_flag & VPP_DECOMPRESSION)) - DBG(5, "Isochronous transfer size: %lu bytes/frame", - (unsigned long)t_size*2) - - DBG(5, "Starting the isochronous transfer...") - - cam->streaming = 1; - - /* Submit the URBs */ - for (i = 0; i < W9968CF_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - cam->streaming = 0; - for (j = i-1; j >= 0; j--) { - usb_kill_urb(cam->urb[j]); - usb_free_urb(cam->urb[j]); - } - DBG(1, "Couldn't send a transfer request to the " - "USB core (error #%d, %s)", err, - symbolic(urb_errlist, err)) - return err; - } - } - - return 0; -} - - -/*-------------------------------------------------------------------------- - Stop the isochronous transfer and set alternate setting to 0 (0Mb/s). - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_stop_transfer(struct w9968cf_device* cam) -{ - struct usb_device *udev = cam->usbdev; - unsigned long lock_flags; - int err = 0; - s8 i; - - if (!cam->streaming) - return 0; - - /* This avoids race conditions with usb_submit_urb() - in the URB completition handler */ - spin_lock_irqsave(&cam->urb_lock, lock_flags); - cam->streaming = 0; - spin_unlock_irqrestore(&cam->urb_lock, lock_flags); - - for (i = W9968CF_URBS-1; i >= 0; i--) - if (cam->urb[i]) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - cam->urb[i] = NULL; - } - - if (cam->disconnected) - goto exit; - - err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */ - err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */ - err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */ - - if (err) { - DBG(2, "Failed to tell the camera to stop the isochronous " - "transfer. However this is not a critical error.") - return -EIO; - } - -exit: - DBG(5, "Isochronous transfer stopped") - return 0; -} - - -/*-------------------------------------------------------------------------- - Write a W9968CF register. - Return 0 on success, -1 otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - int res; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT); - - if (res < 0) - DBG(4, "Failed to write a register " - "(value 0x%04X, index 0x%02X, error #%d, %s)", - value, index, res, symbolic(urb_errlist, res)) - - return (res >= 0) ? 0 : -1; -} - - -/*-------------------------------------------------------------------------- - Read a W9968CF register. - Return the register value on success, -1 otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u16* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT); - - if (res < 0) - DBG(4, "Failed to read a register " - "(index 0x%02X, error #%d, %s)", - index, res, symbolic(urb_errlist, res)) - - return (res >= 0) ? (int)(*buff) : -1; -} - - -/*-------------------------------------------------------------------------- - Write 64-bit data to the fast serial bus registers. - Return 0 on success, -1 otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) -{ - struct usb_device* udev = cam->usbdev; - u16 value; - int res; - - value = *data++; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT); - - if (res < 0) - DBG(4, "Failed to write the FSB registers " - "(error #%d, %s)", res, symbolic(urb_errlist, res)) - - return (res >= 0) ? 0 : -1; -} - - -/*-------------------------------------------------------------------------- - Write data to the serial bus control register. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value) -{ - int err = 0; - - err = w9968cf_write_reg(cam, value, 0x01); - udelay(W9968CF_I2C_BUS_DELAY); - - return err; -} - - -/*-------------------------------------------------------------------------- - Read data from the serial bus control register. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_read_sb(struct w9968cf_device* cam) -{ - int v = 0; - - v = w9968cf_read_reg(cam, 0x01); - udelay(W9968CF_I2C_BUS_DELAY); - - return v; -} - - -/*-------------------------------------------------------------------------- - Upload quantization tables for the JPEG compression. - This function is called by w9968cf_start_transfer(). - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam) -{ - u16 a, b; - int err = 0, i, j; - - err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */ - - for (i = 0, j = 0; i < 32; i++, j += 2) { - a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); - b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); - err += w9968cf_write_reg(cam, a, 0x40+i); - err += w9968cf_write_reg(cam, b, 0x60+i); - } - err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */ - - return err; -} - - - -/**************************************************************************** - * Low-level I2C I/O functions. * - * The adapter supports the following I2C transfer functions: * - * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * - * i2c_adap_read_byte_data() * - * i2c_adap_read_byte() * - ****************************************************************************/ - -static int w9968cf_smbus_start(struct w9968cf_device* cam) -{ - int err = 0; - - err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ - err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ - - return err; -} - - -static int w9968cf_smbus_stop(struct w9968cf_device* cam) -{ - int err = 0; - - err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ - err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ - - return err; -} - - -static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v) -{ - u8 bit; - int err = 0, sda; - - for (bit = 0 ; bit < 8 ; bit++) { - sda = (v & 0x80) ? 2 : 0; - v <<= 1; - /* SDE=1, SDA=sda, SCL=0 */ - err += w9968cf_write_sb(cam, 0x10 | sda); - /* SDE=1, SDA=sda, SCL=1 */ - err += w9968cf_write_sb(cam, 0x11 | sda); - /* SDE=1, SDA=sda, SCL=0 */ - err += w9968cf_write_sb(cam, 0x10 | sda); - } - - return err; -} - - -static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v) -{ - u8 bit; - int err = 0; - - *v = 0; - for (bit = 0 ; bit < 8 ; bit++) { - *v <<= 1; - err += w9968cf_write_sb(cam, 0x0013); - *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0; - err += w9968cf_write_sb(cam, 0x0012); - } - - return err; -} - - -static int w9968cf_smbus_write_ack(struct w9968cf_device* cam) -{ - int err = 0; - - err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ - err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ - err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ - - return err; -} - - -static int w9968cf_smbus_read_ack(struct w9968cf_device* cam) -{ - int err = 0, sda; - - err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ - sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */ - err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */ - if (sda < 0) - err += sda; - if (sda == 1) { - DBG(6, "Couldn't receive the ACK") - err += -1; - } - - return err; -} - - -/* This seems to refresh the communication through the serial bus */ -static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam) -{ - int err = 0, j; - - for (j = 1; j <= 10; j++) { - err = w9968cf_write_reg(cam, 0x0020, 0x01); - err += w9968cf_write_reg(cam, 0x0000, 0x01); - if (err) - break; - } - - return err; -} - - -/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ -static int -w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, - u16 address, u8 subaddress,u8 value) -{ - u16* data = cam->data_buffer; - int err = 0; - - err += w9968cf_smbus_refresh_bus(cam); - - /* Enable SBUS outputs */ - err += w9968cf_write_sb(cam, 0x0020); - - data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0); - data[0] |= (address & 0x40) ? 0x4000 : 0x0; - data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0); - data[1] |= (address & 0x20) ? 0x0150 : 0x0; - data[1] |= (address & 0x10) ? 0x5400 : 0x0; - data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0); - data[2] |= (address & 0x04) ? 0x0540 : 0x0; - data[2] |= (address & 0x02) ? 0x5000 : 0x0; - data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0); - data[3] |= (address & 0x01) ? 0x0054 : 0x0; - - err += w9968cf_write_fsb(cam, data); - - data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0); - data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0; - data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0; - data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0); - data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0; - data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0; - data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0; - data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0); - data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0; - data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0; - data[3] = 0x001d; - - err += w9968cf_write_fsb(cam, data); - - data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); - data[0] |= (value & 0x40) ? 0x0540 : 0x0; - data[0] |= (value & 0x20) ? 0x5000 : 0x0; - data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); - data[1] |= (value & 0x10) ? 0x0054 : 0x0; - data[1] |= (value & 0x08) ? 0x1500 : 0x0; - data[1] |= (value & 0x04) ? 0x4000 : 0x0; - data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); - data[2] |= (value & 0x02) ? 0x0150 : 0x0; - data[2] |= (value & 0x01) ? 0x5400 : 0x0; - data[3] = 0xfe1d; - - err += w9968cf_write_fsb(cam, data); - - /* Disable SBUS outputs */ - err += w9968cf_write_sb(cam, 0x0000); - - if (!err) - DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " - "value 0x%02X", address, subaddress, value) - else - DBG(5, "I2C write byte data failed, addr.0x%04X, " - "subaddr.0x%02X, value 0x%02X", - address, subaddress, value) - - return err; -} - - -/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ -static int -w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, - u16 address, u8 subaddress, - u8* value) -{ - int err = 0; - - /* Serial data enable */ - err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */ - - err += w9968cf_smbus_start(cam); - err += w9968cf_smbus_write_byte(cam, address); - err += w9968cf_smbus_read_ack(cam); - err += w9968cf_smbus_write_byte(cam, subaddress); - err += w9968cf_smbus_read_ack(cam); - err += w9968cf_smbus_stop(cam); - err += w9968cf_smbus_start(cam); - err += w9968cf_smbus_write_byte(cam, address + 1); - err += w9968cf_smbus_read_ack(cam); - err += w9968cf_smbus_read_byte(cam, value); - err += w9968cf_smbus_write_ack(cam); - err += w9968cf_smbus_stop(cam); - - /* Serial data disable */ - err += w9968cf_write_sb(cam, 0x0000); - - if (!err) - DBG(5, "I2C read byte data done, addr.0x%04X, " - "subaddr.0x%02X, value 0x%02X", - address, subaddress, *value) - else - DBG(5, "I2C read byte data failed, addr.0x%04X, " - "subaddr.0x%02X, wrong value 0x%02X", - address, subaddress, *value) - - return err; -} - - -/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */ -static int -w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, - u16 address, u8* value) -{ - int err = 0; - - /* Serial data enable */ - err += w9968cf_write_sb(cam, 0x0013); - - err += w9968cf_smbus_start(cam); - err += w9968cf_smbus_write_byte(cam, address + 1); - err += w9968cf_smbus_read_ack(cam); - err += w9968cf_smbus_read_byte(cam, value); - err += w9968cf_smbus_write_ack(cam); - err += w9968cf_smbus_stop(cam); - - /* Serial data disable */ - err += w9968cf_write_sb(cam, 0x0000); - - if (!err) - DBG(5, "I2C read byte done, addr.0x%04X, " - "value 0x%02X", address, *value) - else - DBG(5, "I2C read byte failed, addr.0x%04X, " - "wrong value 0x%02X", address, *value) - - return err; -} - - -/* SMBus protocol: S Addr Wr [A] Value [A] P */ -static int -w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, - u16 address, u8 value) -{ - DBG(4, "i2c_write_byte() is an unsupported transfer mode") - return -EINVAL; -} - - - -/**************************************************************************** - * I2C interface to kernel * - ****************************************************************************/ - -static int -w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, - unsigned short flags, char read_write, u8 command, - int size, union i2c_smbus_data *data) -{ - struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); - struct w9968cf_device *cam = to_cam(v4l2_dev); - u8 i; - int err = 0; - - if (size == I2C_SMBUS_BYTE) { - /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ - addr <<= 1; - - if (read_write == I2C_SMBUS_WRITE) - err = w9968cf_i2c_adap_write_byte(cam, addr, command); - else if (read_write == I2C_SMBUS_READ) - for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { - err = w9968cf_i2c_adap_read_byte(cam, addr, - &data->byte); - if (err) { - if (w9968cf_smbus_refresh_bus(cam)) { - err = -EIO; - break; - } - } else - break; - } - } else if (size == I2C_SMBUS_BYTE_DATA) { - addr <<= 1; - - if (read_write == I2C_SMBUS_WRITE) - err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, - command, data->byte); - else if (read_write == I2C_SMBUS_READ) { - for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { - err = w9968cf_i2c_adap_read_byte_data(cam,addr, - command, &data->byte); - if (err) { - if (w9968cf_smbus_refresh_bus(cam)) { - err = -EIO; - break; - } - } else - break; - } - - } else - return -EINVAL; - - } else { - DBG(4, "Unsupported I2C transfer mode (%d)", size) - return -EINVAL; - } - return err; -} - - -static u32 w9968cf_i2c_func(struct i2c_adapter* adap) -{ - return I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_BYTE_DATA; -} - - -static int w9968cf_i2c_init(struct w9968cf_device* cam) -{ - int err = 0; - - static struct i2c_algorithm algo = { - .smbus_xfer = w9968cf_i2c_smbus_xfer, - .functionality = w9968cf_i2c_func, - }; - - static struct i2c_adapter adap = { - .owner = THIS_MODULE, - .algo = &algo, - }; - - memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); - strcpy(cam->i2c_adapter.name, "w9968cf"); - cam->i2c_adapter.dev.parent = &cam->usbdev->dev; - i2c_set_adapdata(&cam->i2c_adapter, &cam->v4l2_dev); - - DBG(6, "Registering I2C adapter with kernel...") - - err = i2c_add_adapter(&cam->i2c_adapter); - if (err) - DBG(1, "Failed to register the I2C adapter") - else - DBG(5, "I2C adapter registered") - - return err; -} - - - -/**************************************************************************** - * Helper functions * - ****************************************************************************/ - -/*-------------------------------------------------------------------------- - Turn on the LED on some webcams. A beep should be heard too. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_turn_on_led(struct w9968cf_device* cam) -{ - int err = 0; - - err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */ - err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ - err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ - err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */ - err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */ - err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ - - if (err) - DBG(2, "Couldn't turn on the LED") - - DBG(5, "LED turned on") - - return err; -} - - -/*-------------------------------------------------------------------------- - Write some registers for the device initialization. - This function is called once on open(). - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_init_chip(struct w9968cf_device* cam) -{ - unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2, - y0 = 0x0000, - u0 = y0 + hw_bufsize/2, - v0 = u0 + hw_bufsize/4, - y1 = v0 + hw_bufsize/4, - u1 = y1 + hw_bufsize/2, - v1 = u1 + hw_bufsize/4; - int err = 0; - - err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ - err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */ - - err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */ - err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */ - - err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */ - err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */ - err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */ - err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */ - err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */ - err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V buf.0, high */ - - err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */ - err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */ - err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */ - err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */ - err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */ - err += w9968cf_write_reg(cam, v1 >> 16, 0x2b); /* V buf.1, high */ - - err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */ - err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */ - - err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */ - err += w9968cf_write_reg(cam, y1 >> 16, 0x35); /* JPEG bug 1 high */ - - err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */ - err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ - err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */ - err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */ - - err += w9968cf_set_picture(cam, cam->picture); /* this before */ - err += w9968cf_set_window(cam, cam->window); - - if (err) - DBG(1, "Chip initialization failed") - else - DBG(5, "Chip successfully initialized") - - return err; -} - - -/*-------------------------------------------------------------------------- - Return non-zero if the palette is supported, 0 otherwise. - --------------------------------------------------------------------------*/ -static inline u16 w9968cf_valid_palette(u16 palette) -{ - u8 i = 0; - while (w9968cf_formatlist[i].palette != 0) { - if (palette == w9968cf_formatlist[i].palette) - return palette; - i++; - } - return 0; -} - - -/*-------------------------------------------------------------------------- - Return the depth corresponding to the given palette. - Palette _must_ be supported ! - --------------------------------------------------------------------------*/ -static inline u16 w9968cf_valid_depth(u16 palette) -{ - u8 i=0; - while (w9968cf_formatlist[i].palette != palette) - i++; - - return w9968cf_formatlist[i].depth; -} - - -/*-------------------------------------------------------------------------- - Return non-zero if the format requires decompression, 0 otherwise. - --------------------------------------------------------------------------*/ -static inline u8 w9968cf_need_decompression(u16 palette) -{ - u8 i = 0; - while (w9968cf_formatlist[i].palette != 0) { - if (palette == w9968cf_formatlist[i].palette) - return w9968cf_formatlist[i].compression; - i++; - } - return 0; -} - - -/*-------------------------------------------------------------------------- - Change the picture settings of the camera. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int -w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) -{ - u16 fmt, hw_depth, hw_palette, reg_v = 0x0000; - int err = 0; - - /* Make sure we are using a valid depth */ - pict.depth = w9968cf_valid_depth(pict.palette); - - fmt = pict.palette; - - hw_depth = pict.depth; /* depth used by the winbond chip */ - hw_palette = pict.palette; /* palette used by the winbond chip */ - - /* VS & HS polarities */ - reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11); - - switch (fmt) - { - case VIDEO_PALETTE_UYVY: - reg_v |= 0x0000; - cam->vpp_flag = VPP_NONE; - break; - case VIDEO_PALETTE_YUV422P: - reg_v |= 0x0002; - cam->vpp_flag = VPP_DECOMPRESSION; - break; - case VIDEO_PALETTE_YUV420: - case VIDEO_PALETTE_YUV420P: - reg_v |= 0x0003; - cam->vpp_flag = VPP_DECOMPRESSION; - break; - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_YUV422: - reg_v |= 0x0000; - cam->vpp_flag = VPP_SWAP_YUV_BYTES; - hw_palette = VIDEO_PALETTE_UYVY; - break; - /* Original video is used instead of RGBX palettes. - Software conversion later. */ - case VIDEO_PALETTE_GREY: - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - reg_v |= 0x0000; /* UYVY 16 bit is used */ - hw_depth = 16; - hw_palette = VIDEO_PALETTE_UYVY; - cam->vpp_flag = VPP_UYVY_TO_RGBX; - break; - } - - /* NOTE: due to memory issues, it is better to disable the hardware - double buffering during compression */ - if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) - reg_v |= 0x0080; - - if (cam->clamping) - reg_v |= 0x0020; - - if (cam->filter_type == 1) - reg_v |= 0x0008; - else if (cam->filter_type == 2) - reg_v |= 0x000c; - - if ((err = w9968cf_write_reg(cam, reg_v, 0x16))) - goto error; - - if ((err = w9968cf_sensor_update_picture(cam, pict))) - goto error; - - /* If all went well, update the device data structure */ - memcpy(&cam->picture, &pict, sizeof(pict)); - cam->hw_depth = hw_depth; - cam->hw_palette = hw_palette; - - /* Settings changed, so we clear the frame buffers */ - memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size); - - DBG(4, "Palette is %s, depth is %u bpp", - symbolic(v4l1_plist, pict.palette), pict.depth) - - return 0; - -error: - DBG(1, "Failed to change picture settings") - return err; -} - - -/*-------------------------------------------------------------------------- - Change the capture area size of the camera. - This function _must_ be called _after_ w9968cf_set_picture(). - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int -w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) -{ - u16 x, y, w, h, scx, scy, cw, ch, ax, ay; - unsigned long fw, fh; - struct ovcamchip_window s_win; - int err = 0; - - /* Work around to avoid FP arithmetics */ - #define SC(x) ((x) << 10) - #define UNSC(x) ((x) >> 10) - - /* Make sure we are using a supported resolution */ - if ((err = w9968cf_adjust_window_size(cam, &win.width, &win.height))) - goto error; - - /* Scaling factors */ - fw = SC(win.width) / cam->maxwidth; - fh = SC(win.height) / cam->maxheight; - - /* Set up the width and height values used by the chip */ - if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) { - cam->vpp_flag |= VPP_UPSCALE; - /* Calculate largest w,h mantaining the same w/h ratio */ - w = (fw >= fh) ? cam->maxwidth : SC(win.width)/fh; - h = (fw >= fh) ? SC(win.height)/fw : cam->maxheight; - if (w < cam->minwidth) /* just in case */ - w = cam->minwidth; - if (h < cam->minheight) /* just in case */ - h = cam->minheight; - } else { - cam->vpp_flag &= ~VPP_UPSCALE; - w = win.width; - h = win.height; - } - - /* x,y offsets of the cropped area */ - scx = cam->start_cropx; - scy = cam->start_cropy; - - /* Calculate cropped area manteining the right w/h ratio */ - if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) { - cw = (fw >= fh) ? cam->maxwidth : SC(win.width)/fh; - ch = (fw >= fh) ? SC(win.height)/fw : cam->maxheight; - } else { - cw = w; - ch = h; - } - - /* Setup the window of the sensor */ - s_win.format = VIDEO_PALETTE_UYVY; - s_win.width = cam->maxwidth; - s_win.height = cam->maxheight; - s_win.quarter = 0; /* full progressive video */ - - /* Center it */ - s_win.x = (s_win.width - cw) / 2; - s_win.y = (s_win.height - ch) / 2; - - /* Clock divisor */ - if (cam->clockdiv >= 0) - s_win.clockdiv = cam->clockdiv; /* manual override */ - else - switch (cam->sensor) { - case CC_OV6620: - s_win.clockdiv = 0; - break; - case CC_OV6630: - s_win.clockdiv = 0; - break; - case CC_OV76BE: - case CC_OV7610: - case CC_OV7620: - s_win.clockdiv = 0; - break; - default: - s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR; - } - - /* We have to scale win.x and win.y offsets */ - if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) - || (cam->vpp_flag & VPP_UPSCALE) ) { - ax = SC(win.x)/fw; - ay = SC(win.y)/fh; - } else { - ax = win.x; - ay = win.y; - } - - if ((ax + cw) > cam->maxwidth) - ax = cam->maxwidth - cw; - - if ((ay + ch) > cam->maxheight) - ay = cam->maxheight - ch; - - /* Adjust win.x, win.y */ - if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) - || (cam->vpp_flag & VPP_UPSCALE) ) { - win.x = UNSC(ax*fw); - win.y = UNSC(ay*fh); - } else { - win.x = ax; - win.y = ay; - } - - /* Offsets used by the chip */ - x = ax + s_win.x; - y = ay + s_win.y; - - /* Go ! */ - if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win))) - goto error; - - err += w9968cf_write_reg(cam, scx + x, 0x10); - err += w9968cf_write_reg(cam, scy + y, 0x11); - err += w9968cf_write_reg(cam, scx + x + cw, 0x12); - err += w9968cf_write_reg(cam, scy + y + ch, 0x13); - err += w9968cf_write_reg(cam, w, 0x14); - err += w9968cf_write_reg(cam, h, 0x15); - - /* JPEG width & height */ - err += w9968cf_write_reg(cam, w, 0x30); - err += w9968cf_write_reg(cam, h, 0x31); - - /* Y & UV frame buffer strides (in WORD) */ - if (cam->vpp_flag & VPP_DECOMPRESSION) { - err += w9968cf_write_reg(cam, w/2, 0x2c); - err += w9968cf_write_reg(cam, w/4, 0x2d); - } else - err += w9968cf_write_reg(cam, w, 0x2c); - - if (err) - goto error; - - /* If all went well, update the device data structure */ - memcpy(&cam->window, &win, sizeof(win)); - cam->hw_width = w; - cam->hw_height = h; - - /* Settings changed, so we clear the frame buffers */ - memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size); - - DBG(4, "The capture area is %dx%d, Offset (x,y)=(%u,%u)", - win.width, win.height, win.x, win.y) - - PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, " - "cw=%u, ch=%u, win.x=%u, win.y=%u, win.width=%u, win.height=%u", - x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y, - win.width, win.height) - - return 0; - -error: - DBG(1, "Failed to change the capture area size") - return err; -} - - -/*-------------------------------------------------------------------------- - Adjust the asked values for window width and height. - Return 0 on success, -1 otherwise. - --------------------------------------------------------------------------*/ -static int -w9968cf_adjust_window_size(struct w9968cf_device *cam, u32 *width, u32 *height) -{ - unsigned int maxw, maxh, align; - - maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && - w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) - : cam->maxwidth; - maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && - w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) - : cam->maxheight; - align = (cam->vpp_flag & VPP_DECOMPRESSION) ? 4 : 0; - - v4l_bound_align_image(width, cam->minwidth, maxw, align, - height, cam->minheight, maxh, align, 0); - - PDBGG("Window size adjusted w=%u, h=%u ", *width, *height) - - return 0; -} - - -/*-------------------------------------------------------------------------- - Initialize the FIFO list of requested frames. - --------------------------------------------------------------------------*/ -static void w9968cf_init_framelist(struct w9968cf_device* cam) -{ - u8 i; - - for (i = 0; i < cam->nbuffers; i++) { - cam->requested_frame[i] = NULL; - cam->frame[i].queued = 0; - cam->frame[i].status = F_UNUSED; - } -} - - -/*-------------------------------------------------------------------------- - Add a frame in the FIFO list of requested frames. - This function is called in process context. - --------------------------------------------------------------------------*/ -static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num) -{ - u8 f; - unsigned long lock_flags; - - spin_lock_irqsave(&cam->flist_lock, lock_flags); - - for (f=0; cam->requested_frame[f] != NULL; f++); - cam->requested_frame[f] = &cam->frame[f_num]; - cam->frame[f_num].queued = 1; - cam->frame[f_num].status = F_UNUSED; /* clear the status */ - - spin_unlock_irqrestore(&cam->flist_lock, lock_flags); - - DBG(6, "Frame #%u pushed into the FIFO list. Position %u", f_num, f) -} - - -/*-------------------------------------------------------------------------- - Read, store and remove the first pointer in the FIFO list of requested - frames. This function is called in interrupt context. - --------------------------------------------------------------------------*/ -static void -w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep) -{ - u8 i; - - spin_lock(&cam->flist_lock); - - *framep = cam->requested_frame[0]; - - /* Shift the list of pointers */ - for (i = 0; i < cam->nbuffers-1; i++) - cam->requested_frame[i] = cam->requested_frame[i+1]; - cam->requested_frame[i] = NULL; - - spin_unlock(&cam->flist_lock); - - DBG(6,"Popped frame #%d from the list", (*framep)->number) -} - - -/*-------------------------------------------------------------------------- - High-level video post-processing routine on grabbed frames. - Return 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int -w9968cf_postprocess_frame(struct w9968cf_device* cam, - struct w9968cf_frame_t* fr) -{ - void *pIn = fr->buffer, *pOut = cam->frame_vpp.buffer, *tmp; - u16 w = cam->window.width, - h = cam->window.height, - d = cam->picture.depth, - fmt = cam->picture.palette, - rgb = cam->force_rgb, - hw_w = cam->hw_width, - hw_h = cam->hw_height, - hw_d = cam->hw_depth; - int err = 0; - - #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;} - - if (cam->vpp_flag & VPP_DECOMPRESSION) { - memcpy(pOut, pIn, fr->length); - _PSWAP(pIn, pOut) - err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut); - PDBGG("Compressed frame length: %lu",(unsigned long)fr->length) - fr->length = (hw_w*hw_h*hw_d)/8; - _PSWAP(pIn, pOut) - if (err) { - DBG(4, "An error occurred while decoding the frame: " - "%s", symbolic(decoder_errlist, err)) - return err; - } else - DBG(6, "Frame decoded") - } - - if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) { - w9968cf_vpp->swap_yuvbytes(pIn, fr->length); - DBG(6, "Original UYVY component ordering changed") - } - - if (cam->vpp_flag & VPP_UPSCALE) { - w9968cf_vpp->scale_up(pIn, pOut, hw_w, hw_h, hw_d, w, h); - fr->length = (w*h*hw_d)/8; - _PSWAP(pIn, pOut) - DBG(6, "Vertical up-scaling done: %u,%u,%ubpp->%u,%u", - hw_w, hw_h, hw_d, w, h) - } - - if (cam->vpp_flag & VPP_UYVY_TO_RGBX) { - w9968cf_vpp->uyvy_to_rgbx(pIn, fr->length, pOut, fmt, rgb); - fr->length = (w*h*d)/8; - _PSWAP(pIn, pOut) - DBG(6, "UYVY-16bit to %s conversion done", - symbolic(v4l1_plist, fmt)) - } - - if (pOut == fr->buffer) - memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length); - - return 0; -} - - - -/**************************************************************************** - * Image sensor control routines * - ****************************************************************************/ - -static int -w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val) -{ - struct ovcamchip_control ctl; - int err; - - ctl.id = cid; - ctl.value = val; - - err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl); - - return err; -} - - -static int -w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val) -{ - struct ovcamchip_control ctl; - int err; - - ctl.id = cid; - - err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl); - if (!err) - *val = ctl.value; - - return err; -} - - -static int -w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg) -{ - int rc; - - rc = v4l2_subdev_call(cam->sensor_sd, core, ioctl, cmd, arg); - /* The I2C driver returns -EPERM on non-supported controls */ - return (rc < 0 && rc != -EPERM) ? rc : 0; -} - - -/*-------------------------------------------------------------------------- - Update some settings of the image sensor. - Returns: 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_sensor_update_settings(struct w9968cf_device* cam) -{ - int err = 0; - - /* Auto brightness */ - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, - cam->auto_brt); - if (err) - return err; - - /* Auto exposure */ - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, - cam->auto_exp); - if (err) - return err; - - /* Banding filter */ - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, - cam->bandfilt); - if (err) - return err; - - /* Light frequency */ - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ, - cam->lightfreq); - if (err) - return err; - - /* Back light */ - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT, - cam->backlight); - if (err) - return err; - - /* Mirror */ - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR, - cam->mirror); - if (err) - return err; - - return 0; -} - - -/*-------------------------------------------------------------------------- - Get some current picture settings from the image sensor and update the - internal 'picture' structure of the camera. - Returns: 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_sensor_get_picture(struct w9968cf_device* cam) -{ - int err, v; - - err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v); - if (err) - return err; - cam->picture.contrast = v; - - err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v); - if (err) - return err; - cam->picture.brightness = v; - - err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v); - if (err) - return err; - cam->picture.colour = v; - - err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v); - if (err) - return err; - cam->picture.hue = v; - - DBG(5, "Got picture settings from the image sensor") - - PDBGG("Brightness, contrast, hue, colour, whiteness are " - "%u,%u,%u,%u,%u", cam->picture.brightness,cam->picture.contrast, - cam->picture.hue, cam->picture.colour, cam->picture.whiteness) - - return 0; -} - - -/*-------------------------------------------------------------------------- - Update picture settings of the image sensor. - Returns: 0 on success, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int -w9968cf_sensor_update_picture(struct w9968cf_device* cam, - struct video_picture pict) -{ - int err = 0; - - if ((!cam->sensor_initialized) - || pict.contrast != cam->picture.contrast) { - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT, - pict.contrast); - if (err) - goto fail; - DBG(4, "Contrast changed from %u to %u", - cam->picture.contrast, pict.contrast) - cam->picture.contrast = pict.contrast; - } - - if (((!cam->sensor_initialized) || - pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) { - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, - pict.brightness); - if (err) - goto fail; - DBG(4, "Brightness changed from %u to %u", - cam->picture.brightness, pict.brightness) - cam->picture.brightness = pict.brightness; - } - - if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) { - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, - pict.colour); - if (err) - goto fail; - DBG(4, "Colour changed from %u to %u", - cam->picture.colour, pict.colour) - cam->picture.colour = pict.colour; - } - - if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) { - err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, - pict.hue); - if (err) - goto fail; - DBG(4, "Hue changed from %u to %u", - cam->picture.hue, pict.hue) - cam->picture.hue = pict.hue; - } - - return 0; - -fail: - DBG(4, "Failed to change sensor picture setting") - return err; -} - - - -/**************************************************************************** - * Camera configuration * - ****************************************************************************/ - -/*-------------------------------------------------------------------------- - This function is called when a supported image sensor is detected. - Return 0 if the initialization succeeds, a negative number otherwise. - --------------------------------------------------------------------------*/ -static int w9968cf_sensor_init(struct w9968cf_device* cam) -{ - int err = 0; - - if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, - &cam->monochrome))) - goto error; - - if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, - &cam->sensor))) - goto error; - - /* NOTE: Make sure width and height are a multiple of 16 */ - switch (v4l2_i2c_subdev_addr(cam->sensor_sd)) { - case OV6xx0_SID: - cam->maxwidth = 352; - cam->maxheight = 288; - cam->minwidth = 64; - cam->minheight = 48; - break; - case OV7xx0_SID: - cam->maxwidth = 640; - cam->maxheight = 480; - cam->minwidth = 64; - cam->minheight = 48; - break; - default: - DBG(1, "Not supported image sensor detected for %s", - symbolic(camlist, cam->id)) - return -EINVAL; - } - - /* These values depend on the ones in the ovxxx0.c sources */ - switch (cam->sensor) { - case CC_OV7620: - cam->start_cropx = 287; - cam->start_cropy = 35; - /* Seems to work around a bug in the image sensor */ - cam->vs_polarity = 1; - cam->hs_polarity = 1; - break; - default: - cam->start_cropx = 320; - cam->start_cropy = 35; - cam->vs_polarity = 1; - cam->hs_polarity = 0; - } - - if ((err = w9968cf_sensor_update_settings(cam))) - goto error; - - if ((err = w9968cf_sensor_update_picture(cam, cam->picture))) - goto error; - - cam->sensor_initialized = 1; - - DBG(2, "%s image sensor initialized", symbolic(senlist, cam->sensor)) - return 0; - -error: - cam->sensor_initialized = 0; - cam->sensor = CC_UNKNOWN; - DBG(1, "Image sensor initialization failed for %s (%s). " - "Try to detach and attach this device again", - symbolic(camlist, cam->id), video_device_node_name(cam->v4ldev)) - return err; -} - - -/*-------------------------------------------------------------------------- - Fill some basic fields in the main device data structure. - This function is called once on w9968cf_usb_probe() for each recognized - camera. - --------------------------------------------------------------------------*/ -static void -w9968cf_configure_camera(struct w9968cf_device* cam, - struct usb_device* udev, - enum w9968cf_model_id mod_id, - const unsigned short dev_nr) -{ - mutex_init(&cam->fileop_mutex); - init_waitqueue_head(&cam->open); - spin_lock_init(&cam->urb_lock); - spin_lock_init(&cam->flist_lock); - - cam->users = 0; - cam->disconnected = 0; - cam->id = mod_id; - cam->sensor = CC_UNKNOWN; - cam->sensor_initialized = 0; - - /* Calculate the alternate setting number (from 1 to 16) - according to the 'packet_size' module parameter */ - if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE) - packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE; - for (cam->altsetting = 1; - packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1]; - cam->altsetting++); - - cam->max_buffers = (max_buffers[dev_nr] < 2 || - max_buffers[dev_nr] > W9968CF_MAX_BUFFERS) - ? W9968CF_BUFFERS : (u8)max_buffers[dev_nr]; - - cam->double_buffer = (double_buffer[dev_nr] == 0 || - double_buffer[dev_nr] == 1) - ? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER; - - cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1) - ? (u8)clamping[dev_nr] : W9968CF_CLAMPING; - - cam->filter_type = (filter_type[dev_nr] == 0 || - filter_type[dev_nr] == 1 || - filter_type[dev_nr] == 2) - ? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE; - - cam->capture = 1; - - cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1) - ? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW; - - cam->decompression = (decompression[dev_nr] == 0 || - decompression[dev_nr] == 1 || - decompression[dev_nr] == 2) - ? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION; - - cam->upscaling = (upscaling[dev_nr] == 0 || - upscaling[dev_nr] == 1) - ? (u8)upscaling[dev_nr] : W9968CF_UPSCALING; - - cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1) - ? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT; - - cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1) - ? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP; - - cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60) - ? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ; - - cam->bandfilt = (bandingfilter[dev_nr] == 0 || - bandingfilter[dev_nr] == 1) - ? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER; - - cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1) - ? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT; - - cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0) - ? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV; - - cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1) - ? (u8)mirror[dev_nr] : W9968CF_MIRROR; - - cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1) - ? monochrome[dev_nr] : W9968CF_MONOCHROME; - - cam->picture.brightness = (u16)brightness[dev_nr]; - cam->picture.hue = (u16)hue[dev_nr]; - cam->picture.colour = (u16)colour[dev_nr]; - cam->picture.contrast = (u16)contrast[dev_nr]; - cam->picture.whiteness = (u16)whiteness[dev_nr]; - if (w9968cf_valid_palette((u16)force_palette[dev_nr])) { - cam->picture.palette = (u16)force_palette[dev_nr]; - cam->force_palette = 1; - } else { - cam->force_palette = 0; - if (cam->decompression == 0) - cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF; - else if (cam->decompression == 1) - cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE; - else - cam->picture.palette = W9968CF_PALETTE_DECOMP_ON; - } - cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); - - cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1) - ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB; - - cam->window.x = 0; - cam->window.y = 0; - cam->window.width = W9968CF_WIDTH; - cam->window.height = W9968CF_HEIGHT; - cam->window.chromakey = 0; - cam->window.clipcount = 0; - cam->window.flags = 0; - - DBG(3, "%s configured with settings #%u:", - symbolic(camlist, cam->id), dev_nr) - - DBG(3, "- Data packet size for USB isochrnous transfer: %u bytes", - wMaxPacketSize[cam->altsetting-1]) - - DBG(3, "- Number of requested video frame buffers: %u", - cam->max_buffers) - - if (cam->double_buffer) - DBG(3, "- Hardware double buffering enabled") - else - DBG(3, "- Hardware double buffering disabled") - - if (cam->filter_type == 0) - DBG(3, "- Video filtering disabled") - else if (cam->filter_type == 1) - DBG(3, "- Video filtering enabled: type 1-2-1") - else if (cam->filter_type == 2) - DBG(3, "- Video filtering enabled: type 2-3-6-3-2") - - if (cam->clamping) - DBG(3, "- Video data clamping (CCIR-601 format) enabled") - else - DBG(3, "- Video data clamping (CCIR-601 format) disabled") - - if (cam->largeview) - DBG(3, "- Large view enabled") - else - DBG(3, "- Large view disabled") - - if ((cam->decompression) == 0 && (!cam->force_palette)) - DBG(3, "- Decompression disabled") - else if ((cam->decompression) == 1 && (!cam->force_palette)) - DBG(3, "- Decompression forced") - else if ((cam->decompression) == 2 && (!cam->force_palette)) - DBG(3, "- Decompression allowed") - - if (cam->upscaling) - DBG(3, "- Software image scaling enabled") - else - DBG(3, "- Software image scaling disabled") - - if (cam->force_palette) - DBG(3, "- Image palette forced to %s", - symbolic(v4l1_plist, cam->picture.palette)) - - if (cam->force_rgb) - DBG(3, "- RGB component ordering will be used instead of BGR") - - if (cam->auto_brt) - DBG(3, "- Auto brightness enabled") - else - DBG(3, "- Auto brightness disabled") - - if (cam->auto_exp) - DBG(3, "- Auto exposure enabled") - else - DBG(3, "- Auto exposure disabled") - - if (cam->backlight) - DBG(3, "- Backlight exposure algorithm enabled") - else - DBG(3, "- Backlight exposure algorithm disabled") - - if (cam->mirror) - DBG(3, "- Mirror enabled") - else - DBG(3, "- Mirror disabled") - - if (cam->bandfilt) - DBG(3, "- Banding filter enabled") - else - DBG(3, "- Banding filter disabled") - - DBG(3, "- Power lighting frequency: %u", cam->lightfreq) - - if (cam->clockdiv == -1) - DBG(3, "- Automatic clock divisor enabled") - else - DBG(3, "- Clock divisor: %d", cam->clockdiv) - - if (cam->monochrome) - DBG(3, "- Image sensor used as monochrome") - else - DBG(3, "- Image sensor not used as monochrome") -} - - -/*-------------------------------------------------------------------------- - If the video post-processing module is not loaded, some parameters - must be overridden. - --------------------------------------------------------------------------*/ -static void w9968cf_adjust_configuration(struct w9968cf_device* cam) -{ - if (!w9968cf_vpp) { - if (cam->decompression == 1) { - cam->decompression = 2; - DBG(2, "Video post-processing module not found: " - "'decompression' parameter forced to 2") - } - if (cam->upscaling) { - cam->upscaling = 0; - DBG(2, "Video post-processing module not found: " - "'upscaling' parameter forced to 0") - } - if (cam->picture.palette != VIDEO_PALETTE_UYVY) { - cam->force_palette = 0; - DBG(2, "Video post-processing module not found: " - "'force_palette' parameter forced to 0") - } - cam->picture.palette = VIDEO_PALETTE_UYVY; - cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); - } -} - - -/*-------------------------------------------------------------------------- - Release the resources used by the driver. - This function is called on disconnect - (or on close if deallocation has been deferred) - --------------------------------------------------------------------------*/ -static void w9968cf_release_resources(struct w9968cf_device* cam) -{ - mutex_lock(&w9968cf_devlist_mutex); - - DBG(2, "V4L device deregistered: %s", - video_device_node_name(cam->v4ldev)) - - video_unregister_device(cam->v4ldev); - list_del(&cam->v4llist); - i2c_del_adapter(&cam->i2c_adapter); - w9968cf_deallocate_memory(cam); - kfree(cam->control_buffer); - kfree(cam->data_buffer); - v4l2_device_unregister(&cam->v4l2_dev); - - mutex_unlock(&w9968cf_devlist_mutex); -} - - - -/**************************************************************************** - * Video4Linux interface * - ****************************************************************************/ - -static int w9968cf_open(struct file *filp) -{ - struct w9968cf_device* cam; - int err; - - /* This the only safe way to prevent race conditions with disconnect */ - if (!down_read_trylock(&w9968cf_disconnect)) - return -EAGAIN; - - cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - - mutex_lock(&cam->dev_mutex); - - if (cam->sensor == CC_UNKNOWN) { - DBG(2, "No supported image sensor has been detected by the " - "'ovcamchip' module for the %s (%s). Make sure " - "it is loaded *before* (re)connecting the camera.", - symbolic(camlist, cam->id), - video_device_node_name(cam->v4ldev)) - mutex_unlock(&cam->dev_mutex); - up_read(&w9968cf_disconnect); - return -ENODEV; - } - - if (cam->users) { - DBG(2, "%s (%s) has been already occupied by '%s'", - symbolic(camlist, cam->id), - video_device_node_name(cam->v4ldev), cam->command) - if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { - mutex_unlock(&cam->dev_mutex); - up_read(&w9968cf_disconnect); - return -EWOULDBLOCK; - } - mutex_unlock(&cam->dev_mutex); - err = wait_event_interruptible_exclusive(cam->open, - cam->disconnected || - !cam->users); - if (err) { - up_read(&w9968cf_disconnect); - return err; - } - if (cam->disconnected) { - up_read(&w9968cf_disconnect); - return -ENODEV; - } - mutex_lock(&cam->dev_mutex); - } - - DBG(5, "Opening '%s', %s ...", - symbolic(camlist, cam->id), video_device_node_name(cam->v4ldev)) - - cam->streaming = 0; - cam->misconfigured = 0; - - w9968cf_adjust_configuration(cam); - - if ((err = w9968cf_allocate_memory(cam))) - goto deallocate_memory; - - if ((err = w9968cf_init_chip(cam))) - goto deallocate_memory; - - if ((err = w9968cf_start_transfer(cam))) - goto deallocate_memory; - - filp->private_data = cam; - - cam->users++; - strcpy(cam->command, current->comm); - - init_waitqueue_head(&cam->wait_queue); - - DBG(5, "Video device is open") - - mutex_unlock(&cam->dev_mutex); - up_read(&w9968cf_disconnect); - - return 0; - -deallocate_memory: - w9968cf_deallocate_memory(cam); - DBG(2, "Failed to open the video device") - mutex_unlock(&cam->dev_mutex); - up_read(&w9968cf_disconnect); - return err; -} - - -static int w9968cf_release(struct file *filp) -{ - struct w9968cf_device* cam; - - cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - - mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ - - w9968cf_stop_transfer(cam); - - if (cam->disconnected) { - w9968cf_release_resources(cam); - mutex_unlock(&cam->dev_mutex); - kfree(cam); - return 0; - } - - cam->users--; - w9968cf_deallocate_memory(cam); - wake_up_interruptible_nr(&cam->open, 1); - - DBG(5, "Video device closed") - mutex_unlock(&cam->dev_mutex); - return 0; -} - - -static ssize_t -w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) -{ - struct w9968cf_device* cam; - struct w9968cf_frame_t* fr; - int err = 0; - - cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - - if (filp->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->disconnected) { - DBG(2, "Device not present") - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->misconfigured) { - DBG(2, "The camera is misconfigured. Close and open it again.") - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!cam->frame[0].queued) - w9968cf_push_frame(cam, 0); - - if (!cam->frame[1].queued) - w9968cf_push_frame(cam, 1); - - err = wait_event_interruptible(cam->wait_queue, - cam->frame[0].status == F_READY || - cam->frame[1].status == F_READY || - cam->disconnected); - if (err) { - mutex_unlock(&cam->fileop_mutex); - return err; - } - if (cam->disconnected) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1]; - - if (w9968cf_vpp) - w9968cf_postprocess_frame(cam, fr); - - if (count > fr->length) - count = fr->length; - - if (copy_to_user(buf, fr->buffer, count)) { - fr->status = F_UNUSED; - mutex_unlock(&cam->fileop_mutex); - return -EFAULT; - } - *f_pos += count; - - fr->status = F_UNUSED; - - DBG(5, "%zu bytes read", count) - - mutex_unlock(&cam->fileop_mutex); - return count; -} - - -static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct w9968cf_device* cam = (struct w9968cf_device*) - video_get_drvdata(video_devdata(filp)); - unsigned long vsize = vma->vm_end - vma->vm_start, - psize = cam->nbuffers * cam->frame[0].size, - start = vma->vm_start, - pos = (unsigned long)cam->frame[0].buffer, - page; - - if (cam->disconnected) { - DBG(2, "Device not present") - return -ENODEV; - } - - if (cam->misconfigured) { - DBG(2, "The camera is misconfigured. Close and open it again") - return -EIO; - } - - PDBGG("mmapping %lu bytes...", vsize) - - if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) - return -EINVAL; - - while (vsize > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page + vma->vm_pgoff, - PAGE_SIZE, vma->vm_page_prot)) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - vsize -= PAGE_SIZE; - } - - DBG(5, "mmap method successfully called") - return 0; -} - - -static long -w9968cf_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct w9968cf_device* cam; - long err; - - cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->disconnected) { - DBG(2, "Device not present") - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->misconfigured) { - DBG(2, "The camera is misconfigured. Close and open it again.") - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - err = w9968cf_v4l_ioctl(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - return err; -} - - -static long w9968cf_v4l_ioctl(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct w9968cf_device* cam; - const char* v4l1_ioctls[] = { - "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", - "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", - "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", - "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", - "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", - "GVBIFMT", "SVBIFMT" - }; - - #define V4L1_IOCTL(cmd) \ - ((_IOC_NR((cmd)) < ARRAY_SIZE(v4l1_ioctls)) ? \ - v4l1_ioctls[_IOC_NR((cmd))] : "?") - - cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - - switch (cmd) { - - case VIDIOCGCAP: /* get video capability */ - { - struct video_capability cap = { - .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, - .channels = 1, - .audios = 0, - .minwidth = cam->minwidth, - .minheight = cam->minheight, - }; - sprintf(cap.name, "W996[87]CF USB Camera"); - cap.maxwidth = (cam->upscaling && w9968cf_vpp) - ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) - : cam->maxwidth; - cap.maxheight = (cam->upscaling && w9968cf_vpp) - ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) - : cam->maxheight; - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - DBG(5, "VIDIOCGCAP successfully called") - return 0; - } - - case VIDIOCGCHAN: /* get video channel informations */ - { - struct video_channel chan; - if (copy_from_user(&chan, arg, sizeof(chan))) - return -EFAULT; - - if (chan.channel != 0) - return -EINVAL; - - strcpy(chan.name, "Camera"); - chan.tuners = 0; - chan.flags = 0; - chan.type = VIDEO_TYPE_CAMERA; - chan.norm = VIDEO_MODE_AUTO; - - if (copy_to_user(arg, &chan, sizeof(chan))) - return -EFAULT; - - DBG(5, "VIDIOCGCHAN successfully called") - return 0; - } - - case VIDIOCSCHAN: /* set active channel */ - { - struct video_channel chan; - - if (copy_from_user(&chan, arg, sizeof(chan))) - return -EFAULT; - - if (chan.channel != 0) - return -EINVAL; - - DBG(5, "VIDIOCSCHAN successfully called") - return 0; - } - - case VIDIOCGPICT: /* get image properties of the picture */ - { - if (w9968cf_sensor_get_picture(cam)) - return -EIO; - - if (copy_to_user(arg, &cam->picture, sizeof(cam->picture))) - return -EFAULT; - - DBG(5, "VIDIOCGPICT successfully called") - return 0; - } - - case VIDIOCSPICT: /* change picture settings */ - { - struct video_picture pict; - int err = 0; - - if (copy_from_user(&pict, arg, sizeof(pict))) - return -EFAULT; - - if ( (cam->force_palette || !w9968cf_vpp) - && pict.palette != cam->picture.palette ) { - DBG(4, "Palette %s rejected: only %s is allowed", - symbolic(v4l1_plist, pict.palette), - symbolic(v4l1_plist, cam->picture.palette)) - return -EINVAL; - } - - if (!w9968cf_valid_palette(pict.palette)) { - DBG(4, "Palette %s not supported. VIDIOCSPICT failed", - symbolic(v4l1_plist, pict.palette)) - return -EINVAL; - } - - if (!cam->force_palette) { - if (cam->decompression == 0) { - if (w9968cf_need_decompression(pict.palette)) { - DBG(4, "Decompression disabled: palette %s is not " - "allowed. VIDIOCSPICT failed", - symbolic(v4l1_plist, pict.palette)) - return -EINVAL; - } - } else if (cam->decompression == 1) { - if (!w9968cf_need_decompression(pict.palette)) { - DBG(4, "Decompression forced: palette %s is not " - "allowed. VIDIOCSPICT failed", - symbolic(v4l1_plist, pict.palette)) - return -EINVAL; - } - } - } - - if (pict.depth != w9968cf_valid_depth(pict.palette)) { - DBG(4, "Requested depth %u bpp is not valid for %s " - "palette: ignored and changed to %u bpp", - pict.depth, symbolic(v4l1_plist, pict.palette), - w9968cf_valid_depth(pict.palette)) - pict.depth = w9968cf_valid_depth(pict.palette); - } - - if (pict.palette != cam->picture.palette) { - if(*cam->requested_frame - || cam->frame_current->queued) { - err = wait_event_interruptible - ( cam->wait_queue, - cam->disconnected || - (!*cam->requested_frame && - !cam->frame_current->queued) ); - if (err) - return err; - if (cam->disconnected) - return -ENODEV; - } - - if (w9968cf_stop_transfer(cam)) - goto ioctl_fail; - - if (w9968cf_set_picture(cam, pict)) - goto ioctl_fail; - - if (w9968cf_start_transfer(cam)) - goto ioctl_fail; - - } else if (w9968cf_sensor_update_picture(cam, pict)) - return -EIO; - - - DBG(5, "VIDIOCSPICT successfully called") - return 0; - } - - case VIDIOCSWIN: /* set capture area */ - { - struct video_window win; - int err = 0; - - if (copy_from_user(&win, arg, sizeof(win))) - return -EFAULT; - - DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, " - "x=%u, y=%u, %ux%u", win.clipcount, win.flags, - win.x, win.y, win.width, win.height) - - if (win.clipcount != 0 || win.flags != 0) - return -EINVAL; - - if ((err = w9968cf_adjust_window_size(cam, &win.width, - &win.height))) { - DBG(4, "Resolution not supported (%ux%u). " - "VIDIOCSWIN failed", win.width, win.height) - return err; - } - - if (win.x != cam->window.x || - win.y != cam->window.y || - win.width != cam->window.width || - win.height != cam->window.height) { - if(*cam->requested_frame - || cam->frame_current->queued) { - err = wait_event_interruptible - ( cam->wait_queue, - cam->disconnected || - (!*cam->requested_frame && - !cam->frame_current->queued) ); - if (err) - return err; - if (cam->disconnected) - return -ENODEV; - } - - if (w9968cf_stop_transfer(cam)) - goto ioctl_fail; - - /* This _must_ be called before set_window() */ - if (w9968cf_set_picture(cam, cam->picture)) - goto ioctl_fail; - - if (w9968cf_set_window(cam, win)) - goto ioctl_fail; - - if (w9968cf_start_transfer(cam)) - goto ioctl_fail; - } - - DBG(5, "VIDIOCSWIN successfully called. ") - return 0; - } - - case VIDIOCGWIN: /* get current window properties */ - { - if (copy_to_user(arg,&cam->window,sizeof(struct video_window))) - return -EFAULT; - - DBG(5, "VIDIOCGWIN successfully called") - return 0; - } - - case VIDIOCGMBUF: /* request for memory (mapped) buffer */ - { - struct video_mbuf mbuf; - u8 i; - - mbuf.size = cam->nbuffers * cam->frame[0].size; - mbuf.frames = cam->nbuffers; - for (i = 0; i < cam->nbuffers; i++) - mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer - - (unsigned long)cam->frame[0].buffer; - - if (copy_to_user(arg, &mbuf, sizeof(mbuf))) - return -EFAULT; - - DBG(5, "VIDIOCGMBUF successfully called") - return 0; - } - - case VIDIOCMCAPTURE: /* start the capture to a frame */ - { - struct video_mmap mmap; - struct w9968cf_frame_t* fr; - u32 w, h; - int err = 0; - - if (copy_from_user(&mmap, arg, sizeof(mmap))) - return -EFAULT; - - DBG(6, "VIDIOCMCAPTURE called: frame #%u, format=%s, %dx%d", - mmap.frame, symbolic(v4l1_plist, mmap.format), - mmap.width, mmap.height) - - if (mmap.frame >= cam->nbuffers) { - DBG(4, "Invalid frame number (%u). " - "VIDIOCMCAPTURE failed", mmap.frame) - return -EINVAL; - } - - if (mmap.format!=cam->picture.palette && - (cam->force_palette || !w9968cf_vpp)) { - DBG(4, "Palette %s rejected: only %s is allowed", - symbolic(v4l1_plist, mmap.format), - symbolic(v4l1_plist, cam->picture.palette)) - return -EINVAL; - } - - if (!w9968cf_valid_palette(mmap.format)) { - DBG(4, "Palette %s not supported. " - "VIDIOCMCAPTURE failed", - symbolic(v4l1_plist, mmap.format)) - return -EINVAL; - } - - if (!cam->force_palette) { - if (cam->decompression == 0) { - if (w9968cf_need_decompression(mmap.format)) { - DBG(4, "Decompression disabled: palette %s is not " - "allowed. VIDIOCSPICT failed", - symbolic(v4l1_plist, mmap.format)) - return -EINVAL; - } - } else if (cam->decompression == 1) { - if (!w9968cf_need_decompression(mmap.format)) { - DBG(4, "Decompression forced: palette %s is not " - "allowed. VIDIOCSPICT failed", - symbolic(v4l1_plist, mmap.format)) - return -EINVAL; - } - } - } - - w = mmap.width; h = mmap.height; - err = w9968cf_adjust_window_size(cam, &w, &h); - mmap.width = w; mmap.height = h; - if (err) { - DBG(4, "Resolution not supported (%dx%d). " - "VIDIOCMCAPTURE failed", - mmap.width, mmap.height) - return err; - } - - fr = &cam->frame[mmap.frame]; - - if (mmap.width != cam->window.width || - mmap.height != cam->window.height || - mmap.format != cam->picture.palette) { - - struct video_window win; - struct video_picture pict; - - if(*cam->requested_frame - || cam->frame_current->queued) { - DBG(6, "VIDIOCMCAPTURE. Change settings for " - "frame #%u: %dx%d, format %s. Wait...", - mmap.frame, mmap.width, mmap.height, - symbolic(v4l1_plist, mmap.format)) - err = wait_event_interruptible - ( cam->wait_queue, - cam->disconnected || - (!*cam->requested_frame && - !cam->frame_current->queued) ); - if (err) - return err; - if (cam->disconnected) - return -ENODEV; - } - - memcpy(&win, &cam->window, sizeof(win)); - memcpy(&pict, &cam->picture, sizeof(pict)); - win.width = mmap.width; - win.height = mmap.height; - pict.palette = mmap.format; - - if (w9968cf_stop_transfer(cam)) - goto ioctl_fail; - - /* This before set_window */ - if (w9968cf_set_picture(cam, pict)) - goto ioctl_fail; - - if (w9968cf_set_window(cam, win)) - goto ioctl_fail; - - if (w9968cf_start_transfer(cam)) - goto ioctl_fail; - - } else if (fr->queued) { - - DBG(6, "Wait until frame #%u is free", mmap.frame) - - err = wait_event_interruptible(cam->wait_queue, - cam->disconnected || - (!fr->queued)); - if (err) - return err; - if (cam->disconnected) - return -ENODEV; - } - - w9968cf_push_frame(cam, mmap.frame); - DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame) - return 0; - } - - case VIDIOCSYNC: /* wait until the capture of a frame is finished */ - { - unsigned int f_num; - struct w9968cf_frame_t* fr; - int err = 0; - - if (copy_from_user(&f_num, arg, sizeof(f_num))) - return -EFAULT; - - if (f_num >= cam->nbuffers) { - DBG(4, "Invalid frame number (%u). " - "VIDIOCMCAPTURE failed", f_num) - return -EINVAL; - } - - DBG(6, "VIDIOCSYNC called for frame #%u", f_num) - - fr = &cam->frame[f_num]; - - switch (fr->status) { - case F_UNUSED: - if (!fr->queued) { - DBG(4, "VIDIOSYNC: Frame #%u not requested!", - f_num) - return -EFAULT; - } - case F_ERROR: - case F_GRABBING: - err = wait_event_interruptible(cam->wait_queue, - (fr->status == F_READY) - || cam->disconnected); - if (err) - return err; - if (cam->disconnected) - return -ENODEV; - break; - case F_READY: - break; - } - - if (w9968cf_vpp) - w9968cf_postprocess_frame(cam, fr); - - fr->status = F_UNUSED; - - DBG(5, "VIDIOCSYNC(%u) successfully called", f_num) - return 0; - } - - case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/ - { - struct video_unit unit = { - .video = cam->v4ldev->minor, - .vbi = VIDEO_NO_UNIT, - .radio = VIDEO_NO_UNIT, - .audio = VIDEO_NO_UNIT, - .teletext = VIDEO_NO_UNIT, - }; - - if (copy_to_user(arg, &unit, sizeof(unit))) - return -EFAULT; - - DBG(5, "VIDIOCGUNIT successfully called") - return 0; - } - - case VIDIOCKEY: - return 0; - - case VIDIOCGFBUF: - { - if (clear_user(arg, sizeof(struct video_buffer))) - return -EFAULT; - - DBG(5, "VIDIOCGFBUF successfully called") - return 0; - } - - case VIDIOCGTUNER: - { - struct video_tuner tuner; - if (copy_from_user(&tuner, arg, sizeof(tuner))) - return -EFAULT; - - if (tuner.tuner != 0) - return -EINVAL; - - strcpy(tuner.name, "no_tuner"); - tuner.rangelow = 0; - tuner.rangehigh = 0; - tuner.flags = VIDEO_TUNER_NORM; - tuner.mode = VIDEO_MODE_AUTO; - tuner.signal = 0xffff; - - if (copy_to_user(arg, &tuner, sizeof(tuner))) - return -EFAULT; - - DBG(5, "VIDIOCGTUNER successfully called") - return 0; - } - - case VIDIOCSTUNER: - { - struct video_tuner tuner; - if (copy_from_user(&tuner, arg, sizeof(tuner))) - return -EFAULT; - - if (tuner.tuner != 0) - return -EINVAL; - - if (tuner.mode != VIDEO_MODE_AUTO) - return -EINVAL; - - DBG(5, "VIDIOCSTUNER successfully called") - return 0; - } - - case VIDIOCSFBUF: - case VIDIOCCAPTURE: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - case VIDIOCSPLAYMODE: - case VIDIOCSWRITEMODE: - case VIDIOCGPLAYINFO: - case VIDIOCSMICROCODE: - case VIDIOCGVBIFMT: - case VIDIOCSVBIFMT: - DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s " - "(type 0x%01X, " - "n. 0x%01X, " - "dir. 0x%01X, " - "size 0x%02X)", - V4L1_IOCTL(cmd), - _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) - - return -EINVAL; - - default: - DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s " - "type 0x%01X, " - "n. 0x%01X, " - "dir. 0x%01X, " - "size 0x%02X", - V4L1_IOCTL(cmd), - _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) - - return -ENOIOCTLCMD; - - } /* end of switch */ - -ioctl_fail: - cam->misconfigured = 1; - DBG(1, "VIDIOC%s failed because of hardware problems. " - "To use the camera, close and open it again.", V4L1_IOCTL(cmd)) - return -EFAULT; -} - - -static const struct v4l2_file_operations w9968cf_fops = { - .owner = THIS_MODULE, - .open = w9968cf_open, - .release = w9968cf_release, - .read = w9968cf_read, - .ioctl = w9968cf_ioctl, - .mmap = w9968cf_mmap, -}; - - - -/**************************************************************************** - * USB probe and V4L registration, disconnect and id_table[] definition * - ****************************************************************************/ - -static int -w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct w9968cf_device* cam; - int err = 0; - enum w9968cf_model_id mod_id; - struct list_head* ptr; - u8 sc = 0; /* number of simultaneous cameras */ - static unsigned short dev_nr; /* 0 - we are handling device number n */ - static unsigned short addrs[] = { - OV7xx0_SID, - OV6xx0_SID, - I2C_CLIENT_END - }; - - if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor && - le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct) - mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */ - else if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[1].idVendor && - le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[1].idProduct) - mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */ - else - return -ENODEV; - - cam = (struct w9968cf_device*) - kzalloc(sizeof(struct w9968cf_device), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); - if (err) - goto fail0; - - mutex_init(&cam->dev_mutex); - mutex_lock(&cam->dev_mutex); - - cam->usbdev = udev; - - DBG(2, "%s detected", symbolic(camlist, mod_id)) - - if (simcams > W9968CF_MAX_DEVICES) - simcams = W9968CF_SIMCAMS; - - /* How many cameras are connected ? */ - mutex_lock(&w9968cf_devlist_mutex); - list_for_each(ptr, &w9968cf_dev_list) - sc++; - mutex_unlock(&w9968cf_devlist_mutex); - - if (sc >= simcams) { - DBG(2, "Device rejected: too many connected cameras " - "(max. %u)", simcams) - err = -EPERM; - goto fail; - } - - - /* Allocate 2 bytes of memory for camera control USB transfers */ - if (!(cam->control_buffer = kzalloc(2, GFP_KERNEL))) { - DBG(1,"Couldn't allocate memory for camera control transfers") - err = -ENOMEM; - goto fail; - } - - /* Allocate 8 bytes of memory for USB data transfers to the FSB */ - if (!(cam->data_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "Couldn't allocate memory for data " - "transfers to the FSB") - err = -ENOMEM; - goto fail; - } - - /* Register the V4L device */ - cam->v4ldev = video_device_alloc(); - if (!cam->v4ldev) { - DBG(1, "Could not allocate memory for a V4L structure") - err = -ENOMEM; - goto fail; - } - - strcpy(cam->v4ldev->name, symbolic(camlist, mod_id)); - cam->v4ldev->fops = &w9968cf_fops; - cam->v4ldev->release = video_device_release; - video_set_drvdata(cam->v4ldev, cam); - cam->v4ldev->v4l2_dev = &cam->v4l2_dev; - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L device registration failed") - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(2, "Couldn't find a free /dev/videoX node") - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; - goto fail; - } - - DBG(2, "V4L device registered as %s", - video_device_node_name(cam->v4ldev)) - - /* Set some basic constants */ - w9968cf_configure_camera(cam, udev, mod_id, dev_nr); - - /* Add a new entry into the list of V4L registered devices */ - mutex_lock(&w9968cf_devlist_mutex); - list_add(&cam->v4llist, &w9968cf_dev_list); - mutex_unlock(&w9968cf_devlist_mutex); - dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; - - w9968cf_turn_on_led(cam); - - w9968cf_i2c_init(cam); - cam->sensor_sd = v4l2_i2c_new_subdev(&cam->v4l2_dev, - &cam->i2c_adapter, - "ovcamchip", "ovcamchip", 0, addrs); - - usb_set_intfdata(intf, cam); - mutex_unlock(&cam->dev_mutex); - - err = w9968cf_sensor_init(cam); - return 0; - -fail: /* Free unused memory */ - kfree(cam->control_buffer); - kfree(cam->data_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - mutex_unlock(&cam->dev_mutex); - v4l2_device_unregister(&cam->v4l2_dev); -fail0: - kfree(cam); - return err; -} - - -static void w9968cf_usb_disconnect(struct usb_interface* intf) -{ - struct w9968cf_device* cam = - (struct w9968cf_device*)usb_get_intfdata(intf); - - if (cam) { - down_write(&w9968cf_disconnect); - /* Prevent concurrent accesses to data */ - mutex_lock(&cam->dev_mutex); - - cam->disconnected = 1; - - DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)); - - v4l2_device_disconnect(&cam->v4l2_dev); - - wake_up_interruptible_all(&cam->open); - - if (cam->users) { - DBG(2, "The device is open (%s)! " - "Process name: %s. Deregistration and memory " - "deallocation are deferred on close.", - video_device_node_name(cam->v4ldev), cam->command) - cam->misconfigured = 1; - w9968cf_stop_transfer(cam); - wake_up_interruptible(&cam->wait_queue); - } else - w9968cf_release_resources(cam); - - mutex_unlock(&cam->dev_mutex); - up_write(&w9968cf_disconnect); - - if (!cam->users) { - kfree(cam); - } - } -} - - -static struct usb_driver w9968cf_usb_driver = { - .name = "w9968cf", - .id_table = winbond_id_table, - .probe = w9968cf_usb_probe, - .disconnect = w9968cf_usb_disconnect, -}; - - - -/**************************************************************************** - * Module init, exit and intermodule communication * - ****************************************************************************/ - -static int __init w9968cf_module_init(void) -{ - int err; - - KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) - KDBG(3, W9968CF_MODULE_AUTHOR) - - if ((err = usb_register(&w9968cf_usb_driver))) - return err; - - return 0; -} - - -static void __exit w9968cf_module_exit(void) -{ - /* w9968cf_usb_disconnect() will be called */ - usb_deregister(&w9968cf_usb_driver); - - KDBG(2, W9968CF_MODULE_NAME" deregistered") -} - - -module_init(w9968cf_module_init); -module_exit(w9968cf_module_exit); - diff --git a/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h deleted file mode 100644 index 73ad864b484..00000000000 --- a/drivers/media/video/w9968cf.h +++ /dev/null @@ -1,333 +0,0 @@ -/*************************************************************************** - * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * - * * - * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _W9968CF_H_ -#define _W9968CF_H_ - -#include <linux/videodev2.h> -#include <linux/usb.h> -#include <linux/i2c.h> -#include <linux/device.h> -#include <linux/spinlock.h> -#include <linux/list.h> -#include <linux/wait.h> -#include <linux/param.h> -#include <linux/types.h> -#include <linux/rwsem.h> -#include <linux/mutex.h> - -#include <media/v4l2-device.h> -#include <media/ovcamchip.h> - -#include "w9968cf_vpp.h" - - -/**************************************************************************** - * Default values * - ****************************************************************************/ - -#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ - -/* Comment/uncomment the following line to enable/disable debugging messages */ -#define W9968CF_DEBUG - -/* These have effect only if W9968CF_DEBUG is defined */ -#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */ -#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */ - -#define W9968CF_MAX_DEVICES 32 -#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */ - -#define W9968CF_MAX_BUFFERS 32 -#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */ - -/* Maximum data payload sizes in bytes for alternate settings */ -static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575, - 511, 447, 383, 319, 255, 191, 127, 63}; -#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */ -#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */ -#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */ -#define W9968CF_USB_CTRL_TIMEOUT 1000 /* timeout (ms) for usb control commands */ -#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */ - -#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ -#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */ - -/* Available video formats */ -struct w9968cf_format { - const u16 palette; - const u16 depth; - const u8 compression; -}; - -static const struct w9968cf_format w9968cf_formatlist[] = { - { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */ - { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */ - { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */ - { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */ - { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */ - { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */ - { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */ - { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */ - { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */ - { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */ - { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */ - { 0, 0, 0 } /* 0 is a terminating entry */ -}; - -#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */ -#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */ -#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */ -#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */ - -#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */ - -#define W9968CF_MAX_WIDTH 800 /* Has effect if up-scaling is on */ -#define W9968CF_MAX_HEIGHT 600 /* Has effect if up-scaling is on */ -#define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */ -#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */ - -#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */ -#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */ -#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */ -#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */ -#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */ - -#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */ -#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */ -#define W9968CF_HUE 32768 /* from 0 to 65535 */ -#define W9968CF_COLOUR 32768 /* from 0 to 65535 */ -#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */ -#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */ - -#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */ -#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */ -#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */ -#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */ -#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */ -#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/ - -#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */ -#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */ - - -/**************************************************************************** - * Globals * - ****************************************************************************/ - -#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ - "Dual Mode Camera Chip" -#define W9968CF_MODULE_VERSION "1:1.34-basic" -#define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia" -#define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" -#define W9968CF_MODULE_LICENSE "GPL" - -static const struct usb_device_id winbond_id_table[] = { - { - /* Creative Labs Video Blaster WebCam Go Plus */ - USB_DEVICE(0x041e, 0x4003), - .driver_info = (unsigned long)"w9968cf", - }, - { - /* Generic W996[87]CF JPEG USB Dual Mode Camera */ - USB_DEVICE(0x1046, 0x9967), - .driver_info = (unsigned long)"w9968cf", - }, - { } /* terminating entry */ -}; - -/* W996[87]CF camera models, internal ids: */ -enum w9968cf_model_id { - W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */ - W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/ - W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */ - W9986CF_MOD_AAU = 31, /* AVerMedia AVerTV USB */ - W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */ - W9968CF_MOD_LL = 37, /* Lebon LDC-035A */ - W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */ - W9968CF_MOD_OOE = 42, /* OmniVision OV8610-EDE */ - W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */ - W9968CF_MOD_PDPII = 46, /* Pretec Digi Pen-II */ - W9968CF_MOD_PDP480 = 49, /* Pretec DigiPen-480 */ -}; - -enum w9968cf_frame_status { - F_READY, /* finished grabbing & ready to be read/synced */ - F_GRABBING, /* in the process of being grabbed into */ - F_ERROR, /* something bad happened while processing */ - F_UNUSED /* unused (no VIDIOCMCAPTURE) */ -}; - -struct w9968cf_frame_t { - void* buffer; - unsigned long size; - u32 length; - int number; - enum w9968cf_frame_status status; - struct w9968cf_frame_t* next; - u8 queued; -}; - -enum w9968cf_vpp_flag { - VPP_NONE = 0x00, - VPP_UPSCALE = 0x01, - VPP_SWAP_YUV_BYTES = 0x02, - VPP_DECOMPRESSION = 0x04, - VPP_UYVY_TO_RGBX = 0x08, -}; - -/* Main device driver structure */ -struct w9968cf_device { - enum w9968cf_model_id id; /* private device identifier */ - - struct v4l2_device v4l2_dev; - struct video_device* v4ldev; /* -> V4L structure */ - struct list_head v4llist; /* entry of the list of V4L cameras */ - - struct usb_device* usbdev; /* -> main USB structure */ - struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */ - void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */ - u16* control_buffer; /* -> buffer for control req.*/ - u16* data_buffer; /* -> data to send to the FSB */ - - struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS]; - struct w9968cf_frame_t frame_tmp; /* temporary frame */ - struct w9968cf_frame_t frame_vpp; /* helper frame.*/ - struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */ - struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; - - u8 max_buffers, /* number of requested buffers */ - force_palette, /* yes=1/no=0 */ - force_rgb, /* read RGB instead of BGR, yes=1, no=0 */ - double_buffer, /* hardware double buffering yes=1/no=0 */ - clamping, /* video data clamping yes=1/no=0 */ - filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */ - capture, /* 0=disabled, 1=enabled */ - largeview, /* 0=disabled, 1=enabled */ - decompression, /* 0=disabled, 1=forced, 2=allowed */ - upscaling; /* software image scaling, 0=enabled, 1=disabled */ - - struct video_picture picture; /* current picture settings */ - struct video_window window; /* current window settings */ - - u16 hw_depth, /* depth (used by the chip) */ - hw_palette, /* palette (used by the chip) */ - hw_width, /* width (used by the chip) */ - hw_height, /* height (used by the chip) */ - hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ - vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ - start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/ - start_cropy; /* pixels from VS inactive edge to 1st cropped pixel*/ - - enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */ - - u8 nbuffers, /* number of allocated frame buffers */ - altsetting, /* camera alternate setting */ - disconnected, /* flag: yes=1, no=0 */ - misconfigured, /* flag: yes=1, no=0 */ - users, /* flag: number of users holding the device */ - streaming; /* flag: yes=1, no=0 */ - - u8 sensor_initialized; /* flag: yes=1, no=0 */ - - /* Determined by the image sensor type: */ - int sensor, /* type of image sensor chip (CC_*) */ - monochrome; /* image sensor is (probably) monochrome */ - u16 maxwidth, /* maximum width supported by the image sensor */ - maxheight, /* maximum height supported by the image sensor */ - minwidth, /* minimum width supported by the image sensor */ - minheight; /* minimum height supported by the image sensor */ - u8 auto_brt, /* auto brightness enabled flag */ - auto_exp, /* auto exposure enabled flag */ - backlight, /* backlight exposure algorithm flag */ - mirror, /* image is reversed horizontally */ - lightfreq, /* power (lighting) frequency */ - bandfilt; /* banding filter enabled flag */ - s8 clockdiv; /* clock divisor */ - - /* I2C interface to kernel */ - struct i2c_adapter i2c_adapter; - struct v4l2_subdev *sensor_sd; - - /* Locks */ - struct mutex dev_mutex, /* for probe, disconnect,open and close */ - fileop_mutex; /* for read and ioctl */ - spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */ - flist_lock; /* for requested frame list accesses */ - wait_queue_head_t open, wait_queue; - - char command[16]; /* name of the program holding the device */ -}; - -static inline struct w9968cf_device *to_cam(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct w9968cf_device, v4l2_dev); -} - - -/**************************************************************************** - * Macros for debugging * - ****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef W9968CF_DEBUG -/* For device specific debugging messages */ -# define DBG(level, fmt, args...) \ -{ \ - if ( ((specific_debug) && (debug == (level))) || \ - ((!specific_debug) && (debug >= (level))) ) { \ - if ((level) == 1) \ - v4l2_err(&cam->v4l2_dev, fmt "\n", ## args); \ - else if ((level) == 2 || (level) == 3) \ - v4l2_info(&cam->v4l2_dev, fmt "\n", ## args); \ - else if ((level) == 4) \ - v4l2_warn(&cam->v4l2_dev, fmt "\n", ## args); \ - else if ((level) >= 5) \ - v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} -/* For generic kernel (not device specific) messages */ -# define KDBG(level, fmt, args...) \ -{ \ - if ( ((specific_debug) && (debug == (level))) || \ - ((!specific_debug) && (debug >= (level))) ) { \ - if ((level) >= 1 && (level) <= 4) \ - pr_info("w9968cf: " fmt "\n", ## args); \ - else if ((level) >= 5) \ - pr_debug("w9968cf: [%s:%d] " fmt "\n", __func__, \ - __LINE__ , ## args); \ - } \ -} -#else - /* Not debugging: nothing */ -# define DBG(level, fmt, args...) do {;} while(0); -# define KDBG(level, fmt, args...) do {;} while(0); -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ - -#endif /* _W9968CF_H_ */ diff --git a/drivers/media/video/w9968cf_decoder.h b/drivers/media/video/w9968cf_decoder.h deleted file mode 100644 index 59decbfc540..00000000000 --- a/drivers/media/video/w9968cf_decoder.h +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * Video decoder for the W996[87]CF driver for Linux. * - * * - * Copyright (C) 2003 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _W9968CF_DECODER_H_ -#define _W9968CF_DECODER_H_ - -/* Comment/uncomment this for high/low quality of compressed video */ -#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO - -#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO -static const unsigned char Y_QUANTABLE[64] = { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 -}; - -static const unsigned char UV_QUANTABLE[64] = { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; -#else -static const unsigned char Y_QUANTABLE[64] = { - 8, 5, 5, 8, 12, 20, 25, 30, - 6, 6, 7, 9, 13, 29, 30, 27, - 7, 6, 8, 12, 20, 28, 34, 28, - 7, 8, 11, 14, 25, 43, 40, 31, - 9, 11, 18, 28, 34, 54, 51, 38, - 12, 17, 27, 32, 40, 52, 56, 46, - 24, 32, 39, 43, 51, 60, 60, 50, - 36, 46, 47, 49, 56, 50, 51, 49 -}; - -static const unsigned char UV_QUANTABLE[64] = { - 8, 9, 12, 23, 49, 49, 49, 49, - 9, 10, 13, 33, 49, 49, 49, 49, - 12, 13, 28, 49, 49, 49, 49, 49, - 23, 33, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 -}; -#endif - -#define W9968CF_DEC_ERR_CORRUPTED_DATA -1 -#define W9968CF_DEC_ERR_BUF_OVERFLOW -2 -#define W9968CF_DEC_ERR_NO_SOI -3 -#define W9968CF_DEC_ERR_NO_SOF0 -4 -#define W9968CF_DEC_ERR_NO_SOS -5 -#define W9968CF_DEC_ERR_NO_EOI -6 - -extern void w9968cf_init_decoder(void); -extern int w9968cf_check_headers(const unsigned char* Pin, - const unsigned long BUF_SIZE); -extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE, - const unsigned W, const unsigned H, char* Pout); - -#endif /* _W9968CF_DECODER_H_ */ diff --git a/drivers/media/video/w9968cf_vpp.h b/drivers/media/video/w9968cf_vpp.h deleted file mode 100644 index 88c9b6c0cc3..00000000000 --- a/drivers/media/video/w9968cf_vpp.h +++ /dev/null @@ -1,40 +0,0 @@ -/*************************************************************************** - * Interface for video post-processing functions for the W996[87]CF driver * - * for Linux. * - * * - * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _W9968CF_VPP_H_ -#define _W9968CF_VPP_H_ - -#include <linux/module.h> -#include <asm/types.h> - -struct w9968cf_vpp_t { - struct module* owner; - int (*check_headers)(const unsigned char*, const unsigned long); - int (*decode)(const char*, const unsigned long, const unsigned, - const unsigned, char*); - void (*swap_yuvbytes)(void*, unsigned long); - void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8); - void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16); - - u8 busy; /* read-only flag: module is/is not in use */ -}; - -#endif /* _W9968CF_VPP_H_ */ diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index a11b99b4226..d5965543eca 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -27,11 +27,11 @@ #include <linux/ioctl.h> #include <asm/uaccess.h> #include <linux/i2c.h> -#include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> +#include <media/v4l2-ctrls.h> MODULE_DESCRIPTION("wm8739 driver"); MODULE_AUTHOR("T. Adachi, Hans Verkuil"); @@ -54,12 +54,14 @@ enum { struct wm8739_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* audio cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *mute; + struct v4l2_ctrl *balance; + }; u32 clock_freq; - u8 muted; - u16 volume; - u16 balance; - u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ - u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ }; static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) @@ -67,6 +69,11 @@ static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct wm8739_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; +} + /* ------------------------------------------------------------------------ */ static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) @@ -89,58 +96,17 @@ static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) return -1; } -/* write regs to set audio volume etc */ -static void wm8739_set_audio(struct v4l2_subdev *sd) -{ - struct wm8739_state *state = to_state(sd); - u16 mute = state->muted ? 0x80 : 0; - - /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB - * Default setting: 0x17 = 0 dB - */ - wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute); - wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute); -} - -static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct wm8739_state *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = state->muted; - break; - - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = state->volume; - break; - - case V4L2_CID_AUDIO_BALANCE: - ctrl->value = state->balance; - break; - - default: - return -EINVAL; - } - return 0; -} - -static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct wm8739_state *state = to_state(sd); unsigned int work_l, work_r; + u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u16 mute; switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - state->muted = ctrl->value; - break; - case V4L2_CID_AUDIO_VOLUME: - state->volume = ctrl->value; - break; - - case V4L2_CID_AUDIO_BALANCE: - state->balance = ctrl->value; break; default: @@ -148,52 +114,25 @@ static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ - work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768; - work_r = (min(state->balance, (u16)32768) * state->volume) / 32768; + work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; + work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; - state->vol_l = (long)work_l * 31 / 65535; - state->vol_r = (long)work_r * 31 / 65535; + vol_l = (long)work_l * 31 / 65535; + vol_r = (long)work_r * 31 / 65535; /* set audio volume etc. */ - wm8739_set_audio(sd); + mute = state->mute->val ? 0x80 : 0; + + /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB + * Default setting: 0x17 = 0 dB + */ + wm8739_write(sd, R0, (vol_l & 0x1f) | mute); + wm8739_write(sd, R1, (vol_r & 0x1f) | mute); return 0; } /* ------------------------------------------------------------------------ */ -static struct v4l2_queryctrl wm8739_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 58880, - .flags = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_AUDIO_BALANCE, - .name = "Balance", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .flags = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; - -/* ------------------------------------------------------------------------ */ - static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) { struct wm8739_state *state = to_state(sd); @@ -222,18 +161,6 @@ static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) return 0; } -static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) - if (qc->id && qc->id == wm8739_qctrl[i].id) { - memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); - return 0; - } - return -EINVAL; -} - static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -246,21 +173,26 @@ static int wm8739_log_status(struct v4l2_subdev *sd) struct wm8739_state *state = to_state(sd); v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); - v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f, - state->muted ? " (muted)" : ""); - v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f, - state->muted ? " (muted)" : ""); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); return 0; } /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { + .s_ctrl = wm8739_s_ctrl, +}; + static const struct v4l2_subdev_core_ops wm8739_core_ops = { .log_status = wm8739_log_status, .g_chip_ident = wm8739_g_chip_ident, - .queryctrl = wm8739_queryctrl, - .g_ctrl = wm8739_g_ctrl, - .s_ctrl = wm8739_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { @@ -289,17 +221,28 @@ static int wm8739_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); + state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &wm8739_ops); - state->vol_l = 0x17; /* 0dB */ - state->vol_r = 0x17; /* 0dB */ - state->muted = 0; - state->balance = 32768; - /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ - state->volume = ((long)state->vol_l + 1) * 65535 / 31; + v4l2_ctrl_handler_init(&state->hdl, 2); + state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); + state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, + V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_cluster(3, &state->volume); + state->clock_freq = 48000; /* Initialize wm8739 */ @@ -318,15 +261,17 @@ static int wm8739_probe(struct i2c_client *client, /* activate */ wm8739_write(sd, R9, 0x001); /* set volume/mute */ - wm8739_set_audio(sd); + v4l2_ctrl_handler_setup(&state->hdl); return 0; } static int wm8739_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); kfree(to_state(sd)); return 0; } diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 5c2ba599c0c..23bad3fd6dc 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -35,6 +35,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("wm8775 driver"); @@ -53,8 +54,9 @@ enum { struct wm8775_state { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *mute; u8 input; /* Last selected input (0-0xf) */ - u8 muted; }; static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) @@ -62,6 +64,11 @@ static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct wm8775_state, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; +} + static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -95,7 +102,7 @@ static int wm8775_s_routing(struct v4l2_subdev *sd, return -EINVAL; } state->input = input; - if (state->muted) + if (!v4l2_ctrl_g_ctrl(state->mute)) return 0; wm8775_write(sd, R21, 0x0c0); wm8775_write(sd, R14, 0x1d4); @@ -104,29 +111,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd, return 0; } -static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct wm8775_state *state = to_state(sd); - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; - return 0; -} - -static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct wm8775_state *state = to_state(sd); - - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - wm8775_write(sd, R21, 0x0c0); - wm8775_write(sd, R14, 0x1d4); - wm8775_write(sd, R15, 0x1d4); - if (!state->muted) - wm8775_write(sd, R21, 0x100 + state->input); - return 0; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + if (!ctrl->val) + wm8775_write(sd, R21, 0x100 + state->input); + return 0; + } + return -EINVAL; } static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) @@ -140,8 +139,8 @@ static int wm8775_log_status(struct v4l2_subdev *sd) { struct wm8775_state *state = to_state(sd); - v4l2_info(sd, "Input: %d%s\n", state->input, - state->muted ? " (muted)" : ""); + v4l2_info(sd, "Input: %d\n", state->input); + v4l2_ctrl_handler_log_status(&state->hdl, sd->name); return 0; } @@ -162,11 +161,20 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre /* ----------------------------------------------------------------------- */ +static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { + .s_ctrl = wm8775_s_ctrl, +}; + static const struct v4l2_subdev_core_ops wm8775_core_ops = { .log_status = wm8775_log_status, .g_chip_ident = wm8775_g_chip_ident, - .g_ctrl = wm8775_g_ctrl, - .s_ctrl = wm8775_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { @@ -205,13 +213,24 @@ static int wm8775_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL); + state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &wm8775_ops); state->input = 2; - state->muted = 0; + + v4l2_ctrl_handler_init(&state->hdl, 1); + state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } /* Initialize wm8775 */ @@ -248,9 +267,11 @@ static int wm8775_probe(struct i2c_client *client, static int wm8775_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct wm8775_state *state = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig deleted file mode 100644 index a7e610e0be9..00000000000 --- a/drivers/media/video/zc0301/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config USB_ZC0301 - tristate "USB ZC0301[P] webcam support (DEPRECATED)" - depends on VIDEO_V4L2 - default n - ---help--- - This driver is DEPRECATED please use the gspca zc3xx module - instead. - - Say Y here if you want support for cameras based on the ZC0301 or - ZC0301P Image Processors and Control Chips. - - See <file:Documentation/video4linux/zc0301.txt> for more info. - - To compile this driver as a module, choose M here: the - module will be called zc0301. diff --git a/drivers/media/video/zc0301/Makefile b/drivers/media/video/zc0301/Makefile deleted file mode 100644 index d9e6d97fade..00000000000 --- a/drivers/media/video/zc0301/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -zc0301-objs := zc0301_core.o zc0301_pb0330.o zc0301_pas202bcb.o - -obj-$(CONFIG_USB_ZC0301) += zc0301.o diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h deleted file mode 100644 index b1b5cceb4ba..00000000000 --- a/drivers/media/video/zc0301/zc0301.h +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ZC0301[P] Image Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _ZC0301_H_ -#define _ZC0301_H_ - -#include <linux/version.h> -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <linux/device.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/time.h> -#include <linux/wait.h> -#include <linux/types.h> -#include <linux/param.h> -#include <linux/mutex.h> -#include <linux/rwsem.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/kref.h> - -#include "zc0301_sensor.h" - -/*****************************************************************************/ - -#define ZC0301_DEBUG -#define ZC0301_DEBUG_LEVEL 2 -#define ZC0301_MAX_DEVICES 64 -#define ZC0301_FORCE_MUNMAP 0 -#define ZC0301_MAX_FRAMES 32 -#define ZC0301_COMPRESSION_QUALITY 0 -#define ZC0301_URBS 2 -#define ZC0301_ISO_PACKETS 7 -#define ZC0301_ALTERNATE_SETTING 7 -#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) -#define ZC0301_CTRL_TIMEOUT 100 -#define ZC0301_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -ZC0301_ID_TABLE -ZC0301_SENSOR_TABLE - -enum zc0301_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct zc0301_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum zc0301_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum zc0301_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum zc0301_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum zc0301_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -struct zc0301_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DECLARE_RWSEM(zc0301_dev_lock); - -struct zc0301_device { - struct video_device* v4ldev; - - struct zc0301_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[ZC0301_URBS]; - void* transfer_buffer[ZC0301_URBS]; - u8* control_buffer; - - struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum zc0301_io_method io; - enum zc0301_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct zc0301_module_param module_param; - - struct kref kref; - enum zc0301_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct zc0301_device* -zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - -void -zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor)); -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef ZC0301_DEBUG -# define DBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("zc0301: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ -} while (0) -#else -# define DBG(level, fmt, args...) do {;} while(0) -# define KDBG(level, fmt, args...) do {;} while(0) -# define V4LDBG(level, name, cmd) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ - -#endif /* _ZC0301_H_ */ diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c deleted file mode 100644 index bb51cfb0c64..00000000000 --- a/drivers/media/video/zc0301/zc0301_core.c +++ /dev/null @@ -1,2098 +0,0 @@ -/*************************************************************************** - * Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * Informations about the chip internals needed to enable the I2C protocol * - * have been taken from the documentation of the ZC030x Video4Linux1 * - * driver written by Andrew Birkett <andy@nobugs.org> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/param.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/compiler.h> -#include <linux/ioctl.h> -#include <linux/poll.h> -#include <linux/stat.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/page-flags.h> -#include <asm/byteorder.h> -#include <asm/page.h> -#include <asm/uaccess.h> - -#include "zc0301.h" - -/*****************************************************************************/ - -#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301[P] " \ - "Image Processor and Control Chip" -#define ZC0301_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" -#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" -#define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.10" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 10) - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, zc0301_id_table); - -MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL); -MODULE_DESCRIPTION(ZC0301_MODULE_NAME); -MODULE_VERSION(ZC0301_MODULE_VERSION); -MODULE_LICENSE(ZC0301_MODULE_LICENSE); - -static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(ZC0301_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - "\n<n[,...]> Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." - "\n"); - -#ifdef ZC0301_DEBUG -static unsigned short debug = ZC0301_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - "\n<n> Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." - "\n"); -#endif - -/*****************************************************************************/ - -static u32 -zc0301_request_buffers(struct zc0301_device* cam, u32 count, - enum zc0301_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > ZC0301_MAX_FRAMES) - count = ZC0301_MAX_FRAMES; - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; - } - - return cam->nbuffers; -} - - -static void zc0301_release_buffers(struct zc0301_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void zc0301_empty_framequeues(struct zc0301_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < ZC0301_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void zc0301_requeue_outqueue(struct zc0301_device* cam) -{ - struct zc0301_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void zc0301_queue_unusedframes(struct zc0301_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) -{ - struct usb_device* udev = cam->usbdev; - int res; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, - value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (index 0x%04X, " - "value 0x%02X, error %d)",index, value, res); - return -1; - } - - return 0; -} - - -int zc0301_read_reg(struct zc0301_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, - 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%04X, error %d)", - index, res); - - PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length) -{ - int err = 0, res, r0, r1; - - err += zc0301_write_reg(cam, 0x0092, address); - err += zc0301_write_reg(cam, 0x0090, 0x02); - - msleep(1); - - res = zc0301_read_reg(cam, 0x0091); - if (res < 0) - err += res; - r0 = zc0301_read_reg(cam, 0x0095); - if (r0 < 0) - err += r0; - r1 = zc0301_read_reg(cam, 0x0096); - if (r1 < 0) - err += r1; - - res = (length <= 1) ? r0 : r0 | (r1 << 8); - - if (err) - DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", - address, res); - - - PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); - - return err ? -1 : res; -} - - -int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value) -{ - int err = 0, res; - - err += zc0301_write_reg(cam, 0x0092, address); - err += zc0301_write_reg(cam, 0x0093, value & 0xff); - err += zc0301_write_reg(cam, 0x0094, value >> 8); - err += zc0301_write_reg(cam, 0x0090, 0x01); - - msleep(1); - - res = zc0301_read_reg(cam, 0x0091); - if (res < 0) - err += res; - - if (err) - DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", - address, value); - - PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); - - return err ? -1 : 0; -} - -/*****************************************************************************/ - -static void zc0301_urb_complete(struct urb *urb) -{ - struct zc0301_device* cam = urb->context; - struct zc0301_frame_t** f; - size_t imagesize; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int len, status; - void *pos; - u16* soi; - u8 sof; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - continue; - } - - sof = (*(soi = pos) == 0xd8ff); - - PDBGG("Isochrnous frame: length %u, #%u i,", len, i); - - if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) -start_of_frame: - if (sof) { - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - do_gettimeofday(&(*f)->buf.timestamp); - DBG(3, "SOF detected: new video frame"); - } - - if ((*f)->state == F_GRABBING) { - if (sof && (*f)->buf.bytesused) - goto end_of_frame; - - if ((*f)->buf.bytesused + len > imagesize) { - DBG(3, "Video frame size exceeded"); - (*f)->state = F_ERROR; - continue; - } - - memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); - (*f)->buf.bytesused += len; - - if ((*f)->buf.bytesused == imagesize) { - u32 b; -end_of_frame: - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry(cam->inqueue.next, - struct zc0301_frame_t, - frame); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - DBG(3, "Video frame captured: : %lu bytes", - (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - if (sof) - goto start_of_frame; - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int zc0301_start_transfer(struct zc0301_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - ZC0301_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - struct urb* urb; - s8 i, j; - int err = 0; - - for (i = 0; i < ZC0301_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < ZC0301_URBS; i++) { - urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = ZC0301_ISO_PACKETS; - urb->complete = zc0301_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < ZC0301_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - - for (i = 0; i < ZC0301_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int zc0301_stop_transfer(struct zc0301_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = ZC0301_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int zc0301_stream_interrupt(struct zc0301_device* cam) -{ - long timeout; - - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ZC0301_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. To " - "use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -static int -zc0301_set_compression(struct zc0301_device* cam, - struct v4l2_jpegcompression* compression) -{ - int r, err = 0; - - if ((r = zc0301_read_reg(cam, 0x0008)) < 0) - err += r; - err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality); - - return err ? -EIO : 0; -} - - -static int zc0301_init(struct zc0301_device* cam) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - cam->compression.quality = ZC0301_COMPRESSION_QUALITY; - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - if ((err = zc0301_set_compression(cam, &cam->compression))) { - DBG(3, "set_compression() failed"); - return err; - } - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void zc0301_release_resources(struct kref *kref) -{ - struct zc0301_device *cam = container_of(kref, struct zc0301_device, - kref); - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); -} - - -static int zc0301_open(struct file *filp) -{ - struct zc0301_device* cam; - int err = 0; - - if (!down_read_trylock(&zc0301_dev_lock)) - return -EAGAIN; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&zc0301_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, zc0301_release_resources); - up_read(&zc0301_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is busy...", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&zc0301_dev_lock); - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&zc0301_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = zc0301_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = zc0301_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - zc0301_empty_framequeues(cam); - - DBG(3, "Video device %s is open", - video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, zc0301_release_resources); - up_read(&zc0301_dev_lock); - return err; -} - - -static int zc0301_release(struct file *filp) -{ - struct zc0301_device* cam; - - down_write(&zc0301_dev_lock); - - cam = video_drvdata(filp); - - zc0301_stop_transfer(cam); - zc0301_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", - video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, zc0301_release_resources); - - up_write(&zc0301_dev_lock); - - return 0; -} - - -static ssize_t -zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) -{ - struct zc0301_device *cam = video_drvdata(filp); - struct zc0301_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose the read " - "method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - zc0301_empty_framequeues(cam); - zc0301_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - zc0301_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return err ? err : count; -} - - -static unsigned int zc0301_poll(struct file *filp, poll_table *wait) -{ - struct zc0301_device *cam = video_drvdata(filp); - struct zc0301_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - zc0301_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void zc0301_vm_open(struct vm_area_struct* vma) -{ - struct zc0301_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void zc0301_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct zc0301_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct zc0301_vm_ops = { - .open = zc0301_vm_open, - .close = zc0301_vm_close, -}; - - -static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct zc0301_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &zc0301_vm_ops; - vma->vm_private_data = &cam->frame[i]; - zc0301_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "zc0301", - .version = ZC0301_MODULE_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - return err; -} - - -static int -zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - return 0; -} - - -static int -zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - const enum zc0301_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (!s->set_crop) { - memcpy(rect, &(s->_rect), sizeof(*rect)); - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - return 0; - } - - rect->left &= ~7L; - rect->top &= ~7L; - if (rect->width < 8) - rect->width = 8; - if (rect->height < 8) - rect->height = 8; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - rect->width &= ~7L; - rect->height &= ~7L; - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - zc0301_release_buffers(cam); - - if (s->set_crop) - err += s->set_crop(cam, rect); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width; - s->pix_format.height = rect->height; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - zc0301_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - zc0301_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -zc0301_vidioc_enum_framesizes(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0 && frmsize.index != 1) - return -EINVAL; - - if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG) - return -EINVAL; - - frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE; - - if (frmsize.index == 1) { - frmsize.discrete.width = cam->sensor.cropcap.defrect.width; - frmsize.discrete.height = cam->sensor.cropcap.defrect.height; - } - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "JPEG"); - fmtd.pixelformat = V4L2_PIX_FMT_JPEG; - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->bytesperline = 0; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, - void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - const enum zc0301_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - if (!s->set_crop) { - pix->width = rect.width; - pix->height = rect.height; - } else { - rect.width = pix->width; - rect.height = pix->height; - } - - if (rect.width < 8) - rect.width = 8; - if (rect.height < 8) - rect.height = 8; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - rect.width &= ~7L; - rect.height &= ~7L; - - pix->width = rect.width; - pix->height = rect.height; - pix->pixelformat = pfmt->pixelformat; - pix->priv = pfmt->priv; - pix->colorspace = pfmt->colorspace; - pix->bytesperline = 0; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - zc0301_release_buffers(cam); - - if (s->set_crop) - err += s->set_crop(cam, &rect); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - zc0301_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - zc0301_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum zc0301_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - err += zc0301_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " - "problems. To use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. " - "Previous buffers are still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - zc0301_empty_framequeues(cam); - - zc0301_release_buffers(cam); - if (rb.count) - rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - zc0301_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct zc0301_frame_t *f; - unsigned long lock_flags; - long timeout; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - memcpy(&b, &f->buf, sizeof(b)); - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - zc0301_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES) - sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static long zc0301_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct zc0301_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return zc0301_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return zc0301_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return zc0301_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return zc0301_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return zc0301_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return zc0301_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return zc0301_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return zc0301_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return zc0301_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return zc0301_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FMT: - return zc0301_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return zc0301_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return zc0301_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return zc0301_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_G_JPEGCOMP: - return zc0301_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return zc0301_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return zc0301_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return zc0301_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return zc0301_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return zc0301_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return zc0301_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return zc0301_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return zc0301_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return zc0301_vidioc_s_parm(cam, arg); - - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_QUERYSTD: - case VIDIOC_ENUMSTD: - case VIDIOC_QUERYMENU: - case VIDIOC_ENUM_FRAMEINTERVALS: - return -EINVAL; - - default: - return -EINVAL; - - } -} - - -static long zc0301_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct zc0301_device *cam = video_drvdata(filp); - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "zc0301", cmd); - - err = zc0301_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - - -static const struct v4l2_file_operations zc0301_fops = { - .owner = THIS_MODULE, - .open = zc0301_open, - .release = zc0301_release, - .ioctl = zc0301_ioctl, - .read = zc0301_read, - .poll = zc0301_poll, - .mmap = zc0301_mmap, -}; - -/*****************************************************************************/ - -static int -zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct zc0301_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0; - - if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - DBG(2, "ZC0301[P] Image Processor and Control Chip detected " - "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); - - for (i = 0; zc0301_sensor_table[i]; i++) { - err = zc0301_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) - DBG(2, "%s image sensor detected", cam->sensor.name); - else { - DBG(1, "No supported image sensor detected"); - err = -ENODEV; - goto fail; - } - - if (zc0301_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "ZC0301[P] PC Camera"); - cam->v4ldev->fops = &zc0301_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->parent = &udev->dev; - video_set_drvdata(cam->v4ldev, cam); - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - kfree(cam); - } - return err; -} - - -static void zc0301_usb_disconnect(struct usb_interface* intf) -{ - struct zc0301_device* cam; - - down_write(&zc0301_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and " - "memory deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - zc0301_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - kref_put(&cam->kref, zc0301_release_resources); - - up_write(&zc0301_dev_lock); -} - - -static struct usb_driver zc0301_usb_driver = { - .name = "zc0301", - .id_table = zc0301_id_table, - .probe = zc0301_usb_probe, - .disconnect = zc0301_usb_disconnect, -}; - -/*****************************************************************************/ - -static int __init zc0301_module_init(void) -{ - int err = 0; - - KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION); - KDBG(3, ZC0301_MODULE_AUTHOR); - - if ((err = usb_register(&zc0301_usb_driver))) - KDBG(1, "usb_register() failed"); - - return err; -} - - -static void __exit zc0301_module_exit(void) -{ - usb_deregister(&zc0301_usb_driver); -} - - -module_init(zc0301_module_init); -module_exit(zc0301_module_exit); diff --git a/drivers/media/video/zc0301/zc0301_pas202bcb.c b/drivers/media/video/zc0301/zc0301_pas202bcb.c deleted file mode 100644 index 24b0dfba357..00000000000 --- a/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ /dev/null @@ -1,362 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the ZC0301 Image * - * Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * - * driver maintained by Michel Xhaard <mxhaard@magic.fr> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -/* - NOTE: Sensor controls are disabled for now, becouse changing them while - streaming sometimes results in out-of-sync video frames. We'll use - the default initialization, until we know how to stop and start video - in the chip. However, the image quality still looks good under various - light conditions. -*/ - -#include <linux/delay.h> -#include "zc0301_sensor.h" - - -static struct zc0301_sensor pas202bcb; - - -static int pas202bcb_init(struct zc0301_device* cam) -{ - int err = 0; - - err += zc0301_write_reg(cam, 0x0002, 0x00); - err += zc0301_write_reg(cam, 0x0003, 0x02); - err += zc0301_write_reg(cam, 0x0004, 0x80); - err += zc0301_write_reg(cam, 0x0005, 0x01); - err += zc0301_write_reg(cam, 0x0006, 0xE0); - err += zc0301_write_reg(cam, 0x0098, 0x00); - err += zc0301_write_reg(cam, 0x009A, 0x03); - err += zc0301_write_reg(cam, 0x011A, 0x00); - err += zc0301_write_reg(cam, 0x011C, 0x03); - err += zc0301_write_reg(cam, 0x009B, 0x01); - err += zc0301_write_reg(cam, 0x009C, 0xE6); - err += zc0301_write_reg(cam, 0x009D, 0x02); - err += zc0301_write_reg(cam, 0x009E, 0x86); - - err += zc0301_i2c_write(cam, 0x02, 0x02); - err += zc0301_i2c_write(cam, 0x0A, 0x01); - err += zc0301_i2c_write(cam, 0x0B, 0x01); - err += zc0301_i2c_write(cam, 0x0D, 0x00); - err += zc0301_i2c_write(cam, 0x12, 0x05); - err += zc0301_i2c_write(cam, 0x13, 0x63); - err += zc0301_i2c_write(cam, 0x15, 0x70); - - err += zc0301_write_reg(cam, 0x0101, 0xB7); - err += zc0301_write_reg(cam, 0x0100, 0x0D); - err += zc0301_write_reg(cam, 0x0189, 0x06); - err += zc0301_write_reg(cam, 0x01AD, 0x00); - err += zc0301_write_reg(cam, 0x01C5, 0x03); - err += zc0301_write_reg(cam, 0x01CB, 0x13); - err += zc0301_write_reg(cam, 0x0250, 0x08); - err += zc0301_write_reg(cam, 0x0301, 0x08); - err += zc0301_write_reg(cam, 0x018D, 0x70); - err += zc0301_write_reg(cam, 0x0008, 0x03); - err += zc0301_write_reg(cam, 0x01C6, 0x04); - err += zc0301_write_reg(cam, 0x01CB, 0x07); - err += zc0301_write_reg(cam, 0x0120, 0x11); - err += zc0301_write_reg(cam, 0x0121, 0x37); - err += zc0301_write_reg(cam, 0x0122, 0x58); - err += zc0301_write_reg(cam, 0x0123, 0x79); - err += zc0301_write_reg(cam, 0x0124, 0x91); - err += zc0301_write_reg(cam, 0x0125, 0xA6); - err += zc0301_write_reg(cam, 0x0126, 0xB8); - err += zc0301_write_reg(cam, 0x0127, 0xC7); - err += zc0301_write_reg(cam, 0x0128, 0xD3); - err += zc0301_write_reg(cam, 0x0129, 0xDE); - err += zc0301_write_reg(cam, 0x012A, 0xE6); - err += zc0301_write_reg(cam, 0x012B, 0xED); - err += zc0301_write_reg(cam, 0x012C, 0xF3); - err += zc0301_write_reg(cam, 0x012D, 0xF8); - err += zc0301_write_reg(cam, 0x012E, 0xFB); - err += zc0301_write_reg(cam, 0x012F, 0xFF); - err += zc0301_write_reg(cam, 0x0130, 0x26); - err += zc0301_write_reg(cam, 0x0131, 0x23); - err += zc0301_write_reg(cam, 0x0132, 0x20); - err += zc0301_write_reg(cam, 0x0133, 0x1C); - err += zc0301_write_reg(cam, 0x0134, 0x16); - err += zc0301_write_reg(cam, 0x0135, 0x13); - err += zc0301_write_reg(cam, 0x0136, 0x10); - err += zc0301_write_reg(cam, 0x0137, 0x0D); - err += zc0301_write_reg(cam, 0x0138, 0x0B); - err += zc0301_write_reg(cam, 0x0139, 0x09); - err += zc0301_write_reg(cam, 0x013A, 0x07); - err += zc0301_write_reg(cam, 0x013B, 0x06); - err += zc0301_write_reg(cam, 0x013C, 0x05); - err += zc0301_write_reg(cam, 0x013D, 0x04); - err += zc0301_write_reg(cam, 0x013E, 0x03); - err += zc0301_write_reg(cam, 0x013F, 0x02); - err += zc0301_write_reg(cam, 0x010A, 0x4C); - err += zc0301_write_reg(cam, 0x010B, 0xF5); - err += zc0301_write_reg(cam, 0x010C, 0xFF); - err += zc0301_write_reg(cam, 0x010D, 0xF9); - err += zc0301_write_reg(cam, 0x010E, 0x51); - err += zc0301_write_reg(cam, 0x010F, 0xF5); - err += zc0301_write_reg(cam, 0x0110, 0xFB); - err += zc0301_write_reg(cam, 0x0111, 0xED); - err += zc0301_write_reg(cam, 0x0112, 0x5F); - err += zc0301_write_reg(cam, 0x0180, 0x00); - err += zc0301_write_reg(cam, 0x0019, 0x00); - err += zc0301_write_reg(cam, 0x0087, 0x20); - err += zc0301_write_reg(cam, 0x0088, 0x21); - - err += zc0301_i2c_write(cam, 0x20, 0x02); - err += zc0301_i2c_write(cam, 0x21, 0x1B); - err += zc0301_i2c_write(cam, 0x03, 0x44); - err += zc0301_i2c_write(cam, 0x0E, 0x01); - err += zc0301_i2c_write(cam, 0x0F, 0x00); - - err += zc0301_write_reg(cam, 0x01A9, 0x14); - err += zc0301_write_reg(cam, 0x01AA, 0x24); - err += zc0301_write_reg(cam, 0x0190, 0x00); - err += zc0301_write_reg(cam, 0x0191, 0x02); - err += zc0301_write_reg(cam, 0x0192, 0x1B); - err += zc0301_write_reg(cam, 0x0195, 0x00); - err += zc0301_write_reg(cam, 0x0196, 0x00); - err += zc0301_write_reg(cam, 0x0197, 0x4D); - err += zc0301_write_reg(cam, 0x018C, 0x10); - err += zc0301_write_reg(cam, 0x018F, 0x20); - err += zc0301_write_reg(cam, 0x001D, 0x44); - err += zc0301_write_reg(cam, 0x001E, 0x6F); - err += zc0301_write_reg(cam, 0x001F, 0xAD); - err += zc0301_write_reg(cam, 0x0020, 0xEB); - err += zc0301_write_reg(cam, 0x0087, 0x0F); - err += zc0301_write_reg(cam, 0x0088, 0x0E); - err += zc0301_write_reg(cam, 0x0180, 0x40); - err += zc0301_write_reg(cam, 0x0192, 0x1B); - err += zc0301_write_reg(cam, 0x0191, 0x02); - err += zc0301_write_reg(cam, 0x0190, 0x00); - err += zc0301_write_reg(cam, 0x0116, 0x1D); - err += zc0301_write_reg(cam, 0x0117, 0x40); - err += zc0301_write_reg(cam, 0x0118, 0x99); - err += zc0301_write_reg(cam, 0x0180, 0x42); - err += zc0301_write_reg(cam, 0x0116, 0x1D); - err += zc0301_write_reg(cam, 0x0117, 0x40); - err += zc0301_write_reg(cam, 0x0118, 0x99); - err += zc0301_write_reg(cam, 0x0007, 0x00); - - err += zc0301_i2c_write(cam, 0x11, 0x01); - - msleep(100); - - return err; -} - - -static int pas202bcb_get_ctrl(struct zc0301_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = zc0301_i2c_read(cam, 0x04, 1), - r2 = zc0301_i2c_read(cam, 0x05, 1); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 6) | (r2 & 0x3f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case ZC0301_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case ZC0301_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0) - return -EIO; - return 0; - default: - return -EINVAL; - } -} - - -static int pas202bcb_set_ctrl(struct zc0301_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6); - err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += zc0301_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += zc0301_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += zc0301_i2c_write(cam, 0x10, ctrl->value); - break; - case ZC0301_V4L2_CID_GREEN_BALANCE: - err += zc0301_i2c_write(cam, 0x08, ctrl->value); - break; - case ZC0301_V4L2_CID_DAC_MAGNITUDE: - err += zc0301_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += zc0301_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static struct zc0301_sensor pas202bcb = { - .name = "PAS202BCB", - .init = &pas202bcb_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0c, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x00, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x01, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = ZC0301_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - }, - .get_ctrl = &pas202bcb_get_ctrl, - .set_ctrl = &pas202bcb_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; - - -int zc0301_probe_pas202bcb(struct zc0301_device* cam) -{ - int r0 = 0, r1 = 0, err = 0; - unsigned int pid = 0; - - err += zc0301_write_reg(cam, 0x0000, 0x01); - err += zc0301_write_reg(cam, 0x0010, 0x0e); - err += zc0301_write_reg(cam, 0x0001, 0x01); - err += zc0301_write_reg(cam, 0x0012, 0x03); - err += zc0301_write_reg(cam, 0x0012, 0x01); - err += zc0301_write_reg(cam, 0x008d, 0x08); - - msleep(10); - - r0 = zc0301_i2c_read(cam, 0x00, 1); - r1 = zc0301_i2c_read(cam, 0x01, 1); - - if (r0 < 0 || r1 < 0 || err) - return -EIO; - - pid = (r0 << 4) | ((r1 & 0xf0) >> 4); - if (pid != 0x017) - return -ENODEV; - - zc0301_attach_sensor(cam, &pas202bcb); - - return 0; -} diff --git a/drivers/media/video/zc0301/zc0301_pb0330.c b/drivers/media/video/zc0301/zc0301_pb0330.c deleted file mode 100644 index 9519aba3612..00000000000 --- a/drivers/media/video/zc0301/zc0301_pb0330.c +++ /dev/null @@ -1,188 +0,0 @@ -/*************************************************************************** - * Plug-in for PB-0330 image sensor connected to the ZC0301P Image * - * Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * - * driver maintained by Michel Xhaard <mxhaard@magic.fr> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#include <linux/delay.h> -#include "zc0301_sensor.h" - - -static struct zc0301_sensor pb0330; - - -static int pb0330_init(struct zc0301_device* cam) -{ - int err = 0; - - err += zc0301_write_reg(cam, 0x0000, 0x01); - err += zc0301_write_reg(cam, 0x0008, 0x03); - err += zc0301_write_reg(cam, 0x0010, 0x0A); - err += zc0301_write_reg(cam, 0x0002, 0x00); - err += zc0301_write_reg(cam, 0x0003, 0x02); - err += zc0301_write_reg(cam, 0x0004, 0x80); - err += zc0301_write_reg(cam, 0x0005, 0x01); - err += zc0301_write_reg(cam, 0x0006, 0xE0); - err += zc0301_write_reg(cam, 0x0001, 0x01); - err += zc0301_write_reg(cam, 0x0012, 0x05); - err += zc0301_write_reg(cam, 0x0012, 0x07); - err += zc0301_write_reg(cam, 0x0098, 0x00); - err += zc0301_write_reg(cam, 0x009A, 0x00); - err += zc0301_write_reg(cam, 0x011A, 0x00); - err += zc0301_write_reg(cam, 0x011C, 0x00); - err += zc0301_write_reg(cam, 0x0012, 0x05); - - err += zc0301_i2c_write(cam, 0x01, 0x0006); - err += zc0301_i2c_write(cam, 0x02, 0x0011); - err += zc0301_i2c_write(cam, 0x03, 0x01E7); - err += zc0301_i2c_write(cam, 0x04, 0x0287); - err += zc0301_i2c_write(cam, 0x06, 0x0003); - err += zc0301_i2c_write(cam, 0x07, 0x3002); - err += zc0301_i2c_write(cam, 0x20, 0x1100); - err += zc0301_i2c_write(cam, 0x2F, 0xF7B0); - err += zc0301_i2c_write(cam, 0x30, 0x0005); - err += zc0301_i2c_write(cam, 0x31, 0x0000); - err += zc0301_i2c_write(cam, 0x34, 0x0100); - err += zc0301_i2c_write(cam, 0x35, 0x0060); - err += zc0301_i2c_write(cam, 0x3D, 0x068F); - err += zc0301_i2c_write(cam, 0x40, 0x01E0); - err += zc0301_i2c_write(cam, 0x58, 0x0078); - err += zc0301_i2c_write(cam, 0x62, 0x0411); - - err += zc0301_write_reg(cam, 0x0087, 0x10); - err += zc0301_write_reg(cam, 0x0101, 0x37); - err += zc0301_write_reg(cam, 0x0012, 0x05); - err += zc0301_write_reg(cam, 0x0100, 0x0D); - err += zc0301_write_reg(cam, 0x0189, 0x06); - err += zc0301_write_reg(cam, 0x01AD, 0x00); - err += zc0301_write_reg(cam, 0x01C5, 0x03); - err += zc0301_write_reg(cam, 0x01CB, 0x13); - err += zc0301_write_reg(cam, 0x0250, 0x08); - err += zc0301_write_reg(cam, 0x0301, 0x08); - err += zc0301_write_reg(cam, 0x01A8, 0x60); - err += zc0301_write_reg(cam, 0x018D, 0x6C); - err += zc0301_write_reg(cam, 0x01AD, 0x09); - err += zc0301_write_reg(cam, 0x01AE, 0x15); - err += zc0301_write_reg(cam, 0x010A, 0x50); - err += zc0301_write_reg(cam, 0x010B, 0xF8); - err += zc0301_write_reg(cam, 0x010C, 0xF8); - err += zc0301_write_reg(cam, 0x010D, 0xF8); - err += zc0301_write_reg(cam, 0x010E, 0x50); - err += zc0301_write_reg(cam, 0x010F, 0xF8); - err += zc0301_write_reg(cam, 0x0110, 0xF8); - err += zc0301_write_reg(cam, 0x0111, 0xF8); - err += zc0301_write_reg(cam, 0x0112, 0x50); - err += zc0301_write_reg(cam, 0x0008, 0x03); - err += zc0301_write_reg(cam, 0x01C6, 0x08); - err += zc0301_write_reg(cam, 0x01CB, 0x0F); - err += zc0301_write_reg(cam, 0x010A, 0x50); - err += zc0301_write_reg(cam, 0x010B, 0xF8); - err += zc0301_write_reg(cam, 0x010C, 0xF8); - err += zc0301_write_reg(cam, 0x010D, 0xF8); - err += zc0301_write_reg(cam, 0x010E, 0x50); - err += zc0301_write_reg(cam, 0x010F, 0xF8); - err += zc0301_write_reg(cam, 0x0110, 0xF8); - err += zc0301_write_reg(cam, 0x0111, 0xF8); - err += zc0301_write_reg(cam, 0x0112, 0x50); - err += zc0301_write_reg(cam, 0x0180, 0x00); - err += zc0301_write_reg(cam, 0x0019, 0x00); - - err += zc0301_i2c_write(cam, 0x05, 0x0066); - err += zc0301_i2c_write(cam, 0x09, 0x02B2); - err += zc0301_i2c_write(cam, 0x10, 0x0002); - - err += zc0301_write_reg(cam, 0x011D, 0x60); - err += zc0301_write_reg(cam, 0x0190, 0x00); - err += zc0301_write_reg(cam, 0x0191, 0x07); - err += zc0301_write_reg(cam, 0x0192, 0x8C); - err += zc0301_write_reg(cam, 0x0195, 0x00); - err += zc0301_write_reg(cam, 0x0196, 0x00); - err += zc0301_write_reg(cam, 0x0197, 0x8A); - err += zc0301_write_reg(cam, 0x018C, 0x10); - err += zc0301_write_reg(cam, 0x018F, 0x20); - err += zc0301_write_reg(cam, 0x01A9, 0x14); - err += zc0301_write_reg(cam, 0x01AA, 0x24); - err += zc0301_write_reg(cam, 0x001D, 0xD7); - err += zc0301_write_reg(cam, 0x001E, 0xF0); - err += zc0301_write_reg(cam, 0x001F, 0xF8); - err += zc0301_write_reg(cam, 0x0020, 0xFF); - err += zc0301_write_reg(cam, 0x01AD, 0x09); - err += zc0301_write_reg(cam, 0x01AE, 0x15); - err += zc0301_write_reg(cam, 0x0180, 0x40); - err += zc0301_write_reg(cam, 0x0180, 0x42); - - msleep(100); - - return err; -} - - -static struct zc0301_sensor pb0330 = { - .name = "PB-0330", - .init = &pb0330_init, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; - - -int zc0301_probe_pb0330(struct zc0301_device* cam) -{ - int r0, err = 0; - - err += zc0301_write_reg(cam, 0x0000, 0x01); - err += zc0301_write_reg(cam, 0x0010, 0x0a); - err += zc0301_write_reg(cam, 0x0001, 0x01); - err += zc0301_write_reg(cam, 0x0012, 0x03); - err += zc0301_write_reg(cam, 0x0012, 0x01); - - msleep(10); - - r0 = zc0301_i2c_read(cam, 0x00, 2); - - if (r0 < 0 || err) - return -EIO; - - if (r0 != 0x8243) - return -ENODEV; - - zc0301_attach_sensor(cam, &pb0330); - - return 0; -} diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h deleted file mode 100644 index 0be783c203f..00000000000 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to the ZC0301[P] Image Processor and * - * Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _ZC0301_SENSOR_H_ -#define _ZC0301_SENSOR_H_ - -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <linux/device.h> -#include <linux/stddef.h> -#include <linux/errno.h> -#include <asm/types.h> - -struct zc0301_device; -struct zc0301_sensor; - -/*****************************************************************************/ - -extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); -extern int zc0301_probe_pb0330(struct zc0301_device* cam); - -#define ZC0301_SENSOR_TABLE \ -/* Weak detections must go at the end of the list */ \ -static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ - &zc0301_probe_pas202bcb, \ - &zc0301_probe_pb0330, \ - NULL, \ -}; - -extern struct zc0301_device* -zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); - -extern void -zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); - -#define ZC0301_USB_DEVICE(vend, prod, intclass) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = (intclass) - -#if !defined CONFIG_USB_GSPCA_ZC3XX && !defined CONFIG_USB_GSPCA_ZC3XX_MODULE -#define ZC0301_ID_TABLE \ -static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ - { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ - { } \ -}; -#else -#define ZC0301_ID_TABLE \ -static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ - { } \ -}; -#endif - -/*****************************************************************************/ - -extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value); -extern int zc0301_read_reg(struct zc0301_device*, u16 index); -extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value); -extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); - -/*****************************************************************************/ - -#define ZC0301_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) -#define ZC0301_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) -#define ZC0301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) - -struct zc0301_sensor { - char name[32]; - - struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS]; - struct v4l2_cropcap cropcap; - struct v4l2_pix_format pix_format; - - int (*init)(struct zc0301_device*); - int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); - int (*set_ctrl)(struct zc0301_device*, - const struct v4l2_control* ctrl); - int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); - - /* Private */ - struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -#endif /* _ZC0301_SENSOR_H_ */ diff --git a/drivers/media/video/zoran/videocodec.c b/drivers/media/video/zoran/videocodec.c index cf24956f320..c0107163529 100644 --- a/drivers/media/video/zoran/videocodec.c +++ b/drivers/media/video/zoran/videocodec.c @@ -107,15 +107,14 @@ videocodec_attach (struct videocodec_master *master) if (!try_module_get(h->codec->owner)) return NULL; - codec = - kmalloc(sizeof(struct videocodec), GFP_KERNEL); + codec = kmemdup(h->codec, sizeof(struct videocodec), + GFP_KERNEL); if (!codec) { dprintk(1, KERN_ERR "videocodec_attach: no mem\n"); goto out_module_put; } - memcpy(codec, h->codec, sizeof(struct videocodec)); snprintf(codec->name, sizeof(codec->name), "%s[%d]", codec->name, h->attached); diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h index 8997add1248..307e847fe1c 100644 --- a/drivers/media/video/zoran/zoran.h +++ b/drivers/media/video/zoran/zoran.h @@ -391,7 +391,7 @@ struct zoran { struct mutex resource_lock; /* prevent evil stuff */ - u8 initialized; /* flag if zoran has been correctly initalized */ + u8 initialized; /* flag if zoran has been correctly initialized */ int user; /* number of current users */ struct card_info card; struct tvnorm *timing; diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index e6ad4b20561..6f846abee3e 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -484,7 +484,7 @@ zr36057_overlay (struct zoran *zr, zr->overlay_settings.format); /* Start and length of each line MUST be 4-byte aligned. - * This should be allready checked before the call to this routine. + * This should be already checked before the call to this routine. * All error messages are internal driver checking only! */ /* video display top and bottom registers */ diff --git a/drivers/media/video/zoran/zr36050.c b/drivers/media/video/zoran/zr36050.c index 639dd87c663..e1985609af4 100644 --- a/drivers/media/video/zoran/zr36050.c +++ b/drivers/media/video/zoran/zr36050.c @@ -236,7 +236,7 @@ zr36050_pushit (struct zr36050 *ptr, Could be variable, but until it's not needed it they are just fixed to save memory. Otherwise expand zr36050 structure with arrays, push the values to - it and initalize from there, as e.g. the linux zr36057/60 driver does it. + it and initialize from there, as e.g. the linux zr36057/60 driver does it. ========================================================================= */ static const char zr36050_dqt[0x86] = { diff --git a/drivers/media/video/zoran/zr36060.c b/drivers/media/video/zoran/zr36060.c index 008746ff774..5e4f57cbf31 100644 --- a/drivers/media/video/zoran/zr36060.c +++ b/drivers/media/video/zoran/zr36060.c @@ -227,7 +227,7 @@ zr36060_pushit (struct zr36060 *ptr, Could be variable, but until it's not needed it they are just fixed to save memory. Otherwise expand zr36060 structure with arrays, push the values to - it and initalize from there, as e.g. the linux zr36057/60 driver does it. + it and initialize from there, as e.g. the linux zr36057/60 driver does it. ========================================================================= */ static const char zr36060_dqt[0x86] = { |