diff options
Diffstat (limited to 'drivers/media/video')
158 files changed, 13974 insertions, 5626 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9644cf760aa..ad9e6f9c22e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -45,6 +45,10 @@ config VIDEO_TUNER tristate depends on MEDIA_TUNER +config V4L2_MEM2MEM_DEV + tristate + depends on VIDEOBUF_GEN + # # Multimedia Video device configuration # @@ -480,6 +484,12 @@ config VIDEO_ADV7343 To compile this driver as a module, choose M here: the module will be called adv7343. +config VIDEO_AK881X + tristate "AK8813/AK8814 video encoders" + depends on I2C + help + Video output driver for AKM AK8813 and AK8814 TV encoders + comment "Video improvement chips" config VIDEO_UPD64031A @@ -520,6 +530,13 @@ config DISPLAY_DAVINCI_DM646X_EVM 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 + select VIDEOBUF_DMA_CONTIG + 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 @@ -542,7 +559,8 @@ config VIDEO_DAVINCI_VPIF config VIDEO_VIVI tristate "Virtual Video Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FONTS + select FONT_8x16 select VIDEOBUF_VMALLOC default n ---help--- @@ -613,6 +631,8 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. +source "drivers/media/video/omap/Kconfig" + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS @@ -647,7 +667,7 @@ config VIDEO_CQCAM config VIDEO_W9966 tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux" - depends on PARPORT_1284 && PARPORT && VIDEO_V4L1 + depends on PARPORT_1284 && PARPORT && VIDEO_V4L2 help Video4linux driver for Winbond's w9966 based Webcams. Currently tested with the LifeView FlyCam Supra. @@ -740,7 +760,7 @@ source "drivers/media/video/zoran/Kconfig" config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && SONY_LAPTOP && VIDEO_V4L1 + depends on PCI && SONY_LAPTOP && VIDEO_V4L2 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in @@ -807,7 +827,7 @@ source "drivers/media/video/saa7164/Kconfig" config VIDEO_M32R_AR tristate "AR devices" - depends on M32R && VIDEO_V4L1 + depends on M32R && VIDEO_V4L2 ---help--- This is a video4linux driver for the Renesas AR (Artificial Retina) camera module. @@ -1107,3 +1127,27 @@ config USB_S2255 endif # V4L_USB_DRIVERS endif # VIDEO_CAPTURE_DRIVERS + +menuconfig V4L_MEM2MEM_DRIVERS + bool "Memory-to-memory multimedia devices" + depends on VIDEO_V4L2 + default n + ---help--- + Say Y here to enable selecting drivers for V4L devices that + use system memory for both source and destination buffers, as opposed + to capture and output drivers, which use memory buffers for just + one of those. + +if V4L_MEM2MEM_DRIVERS + +config VIDEO_MEM2MEM_TESTDEV + tristate "Virtual test device for mem2mem framework" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF_VMALLOC + select V4L2_MEM2MEM_DEV + default n + ---help--- + This is a virtual test device for the memory-to-memory driver + framework. + +endif # V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index c51c386559f..cc93859d316 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -10,7 +10,8 @@ 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 +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ + v4l2-event.o # V4L2 core modules @@ -117,6 +118,8 @@ obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o + obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o @@ -149,8 +152,11 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o +obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ +obj-$(CONFIG_VIDEO_AK881X) += ak881x.o + obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o @@ -160,6 +166,10 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + +obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o + obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ @@ -169,6 +179,8 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_ARCH_DAVINCI) += davinci/ +obj-$(CONFIG_ARCH_OMAP) += omap/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c new file mode 100644 index 00000000000..35390d4717b --- /dev/null +++ b/drivers/media/video/ak881x.c @@ -0,0 +1,368 @@ +/* + * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) + * + * 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/i2c.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/videodev2.h> + +#include <media/ak881x.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> + +#define AK881X_INTERFACE_MODE 0 +#define AK881X_VIDEO_PROCESS1 1 +#define AK881X_VIDEO_PROCESS2 2 +#define AK881X_VIDEO_PROCESS3 3 +#define AK881X_DAC_MODE 5 +#define AK881X_STATUS 0x24 +#define AK881X_DEVICE_ID 0x25 +#define AK881X_DEVICE_REVISION 0x26 + +struct ak881x { + struct v4l2_subdev subdev; + struct ak881x_pdata *pdata; + unsigned int lines; + int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ + char revision; /* DEVICE_REVISION content */ +}; + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u8 data) +{ + return i2c_smbus_write_byte_data(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u8 data, u8 mask) +{ + int ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static struct ak881x *to_ak881x(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ak881x, subdev); +} + +static int ak881x_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = ak881x->id; + id->revision = ak881x->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ak881x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int ak881x_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + 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->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +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) + return -EINVAL; + + return ak881x_try_g_mbus_fmt(sd, mf); +} + +static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + return 0; +} + +static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = 720; + a->bounds.height = ak881x->lines; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + u8 vp1; + + if (std == V4L2_STD_NTSC_443) { + vp1 = 3; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_M) { + vp1 = 5; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_60) { + vp1 = 7; + ak881x->lines = 480; + } else if (std && !(std & ~V4L2_STD_PAL)) { + vp1 = 0xf; + ak881x->lines = 576; + } else if (std && !(std & ~V4L2_STD_NTSC)) { + vp1 = 0; + ak881x->lines = 480; + } else { + /* No SECAM or PAL_N/Nc supported */ + return -EINVAL; + } + + reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); + + return 0; +} + +static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (enable) { + u8 dac; + /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ + /* Default: composite output */ + if (ak881x->pdata->flags & AK881X_COMPONENT) + dac = 3; + else + dac = 4; + /* Turn on the DAC(s) */ + reg_write(client, AK881X_DAC_MODE, dac); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } else { + /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ + reg_write(client, AK881X_DAC_MODE, 0); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } + + return 0; +} + +static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { + .g_chip_ident = ak881x_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ak881x_g_register, + .s_register = ak881x_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { + .s_mbus_fmt = ak881x_s_mbus_fmt, + .g_mbus_fmt = ak881x_try_g_mbus_fmt, + .try_mbus_fmt = ak881x_try_g_mbus_fmt, + .cropcap = ak881x_cropcap, + .enum_mbus_fmt = ak881x_enum_mbus_fmt, + .s_std_output = ak881x_s_std_output, + .s_stream = ak881x_s_stream, +}; + +static struct v4l2_subdev_ops ak881x_subdev_ops = { + .core = &ak881x_subdev_core_ops, + .video = &ak881x_subdev_video_ops, +}; + +static int ak881x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct ak881x *ak881x; + u8 ifmode, data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); + if (!ak881x) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); + + data = reg_read(client, AK881X_DEVICE_ID); + + switch (data) { + case 0x13: + ak881x->id = V4L2_IDENT_AK8813; + break; + case 0x14: + ak881x->id = V4L2_IDENT_AK8814; + break; + default: + dev_err(&client->dev, + "No ak881x chip detected, register read %x\n", data); + kfree(ak881x); + return -ENODEV; + } + + ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); + ak881x->pdata = client->dev.platform_data; + + if (ak881x->pdata) { + if (ak881x->pdata->flags & AK881X_FIELD) + ifmode = 4; + else + ifmode = 0; + + switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { + case AK881X_IF_MODE_BT656: + ifmode |= 1; + break; + case AK881X_IF_MODE_MASTER: + ifmode |= 2; + break; + case AK881X_IF_MODE_SLAVE: + default: + break; + } + + dev_dbg(&client->dev, "IF mode %x\n", ifmode); + + /* + * "Line Blanking No." seems to be the same as the number of + * "black" lines on, e.g., SuperH VOU, whose default value of 20 + * "incidentally" matches ak881x' default + */ + reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); + } + + /* Hardware default: NTSC-M */ + ak881x->lines = 480; + + dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", + data, ak881x->revision); + + return 0; +} + +static int ak881x_remove(struct i2c_client *client) +{ + struct ak881x *ak881x = to_ak881x(client); + + v4l2_device_unregister_subdev(&ak881x->subdev); + kfree(ak881x); + + return 0; +} + +static const struct i2c_device_id ak881x_id[] = { + { "ak8813", 0 }, + { "ak8814", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak881x_id); + +static struct i2c_driver ak881x_i2c_driver = { + .driver = { + .name = "ak881x", + }, + .probe = ak881x_probe, + .remove = ak881x_remove, + .id_table = ak881x_id, +}; + +static int __init ak881x_module_init(void) +{ + return i2c_add_driver(&ak881x_i2c_driver); +} + +static void __exit ak881x_module_exit(void) +{ + i2c_del_driver(&ak881x_i2c_driver); +} + +module_init(ak881x_module_init); +module_exit(ak881x_module_exit); + +MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index a356d6bd313..31e7a123d19 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -27,8 +27,10 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/sched.h> -#include <linux/videodev.h> +#include <linux/version.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <linux/mutex.h> @@ -39,7 +41,7 @@ #include <asm/byteorder.h> #if 0 -#define DEBUG(n, args...) printk(args) +#define DEBUG(n, args...) printk(KERN_INFO args) #define CHECK_LOST 1 #else #define DEBUG(n, args...) @@ -52,10 +54,10 @@ */ #define USE_INT 0 /* Don't modify */ -#define VERSION "0.03" +#define VERSION "0.04" #define ar_inl(addr) inl((unsigned long)(addr)) -#define ar_outl(val, addr) outl((unsigned long)(val),(unsigned long)(addr)) +#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr)) extern struct cpuinfo_m32r boot_cpu_data; @@ -79,7 +81,7 @@ extern struct cpuinfo_m32r boot_cpu_data; /* bits & bytes per pixel */ #define AR_BITS_PER_PIXEL 16 -#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL/8) +#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8) /* line buffer size */ #define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL) @@ -104,8 +106,9 @@ extern struct cpuinfo_m32r boot_cpu_data; #define AR_MODE_INTERLACE 0 #define AR_MODE_NORMAL 1 -struct ar_device { - struct video_device *vdev; +struct ar { + struct v4l2_device v4l2_dev; + struct video_device vdev; unsigned int start_capture; /* duaring capture in INT. mode. */ #if USE_INT unsigned char *line_buff; /* DMA line buffer */ @@ -116,12 +119,13 @@ struct ar_device { int width, height; int frame_bytes, line_bytes; wait_queue_head_t wait; - unsigned long in_use; struct mutex lock; }; +static struct ar ardev; + static int video_nr = -1; /* video device number (first free) */ -static unsigned char yuv[MAX_AR_FRAME_BYTES]; +static unsigned char yuv[MAX_AR_FRAME_BYTES]; /* module parameters */ /* default frequency */ @@ -133,9 +137,7 @@ module_param(freq, int, 0); module_param(vga, int, 0); module_param(vga_interlace, int, 0); -static int ar_initialize(struct video_device *dev); - -static inline void wait_for_vsync(void) +static void wait_for_vsync(void) { while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */ cpu_relax(); @@ -143,7 +145,7 @@ static inline void wait_for_vsync(void) cpu_relax(); } -static inline void wait_acknowledge(void) +static void wait_acknowledge(void) { int i; @@ -156,7 +158,7 @@ static inline void wait_acknowledge(void) /******************************************************************* * I2C functions *******************************************************************/ -void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, +static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, unsigned long data3) { int i; @@ -200,7 +202,7 @@ void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, } -void init_iic(void) +static void init_iic(void) { DEBUG(1, "init_iic:\n"); @@ -214,13 +216,12 @@ void init_iic(void) /* I2C CLK */ /* 50MH-100k */ - if (freq == 75) { + if (freq == 75) ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */ - } else if (freq == 50) { + else if (freq == 50) ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */ - } else { + else ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */ - } ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */ } @@ -245,7 +246,7 @@ static inline void clear_dma_status(void) ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */ } -static inline void wait_for_vertical_sync(int exp_line) +static void wait_for_vertical_sync(struct ar *ar, int exp_line) { #if CHECK_LOST int tmout = 10000; /* FIXME */ @@ -260,7 +261,7 @@ static inline void wait_for_vertical_sync(int exp_line) break; } if (tmout < 0) - printk("arv: lost %d -> %d\n", exp_line, l); + v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l); #else while (ar_inl(ARVHCOUNT) != exp_line) cpu_relax(); @@ -269,15 +270,14 @@ static inline void wait_for_vertical_sync(int exp_line) static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct video_device *v = video_devdata(file); - struct ar_device *ar = video_get_drvdata(v); + struct ar *ar = video_drvdata(file); long ret = ar->frame_bytes; /* return read bytes */ unsigned long arvcr1 = 0; unsigned long flags; unsigned char *p; int h, w; unsigned char *py, *pu, *pv; -#if ! USE_INT +#if !USE_INT int l; #endif @@ -305,7 +305,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */ /* - * Okey , kicks AR LSI to invoke an interrupt + * Okay, kick AR LSI to invoke an interrupt */ ar->start_capture = 0; ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1); @@ -313,7 +313,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) /* .... AR interrupts .... */ interruptible_sleep_on(&ar->wait); if (signal_pending(current)) { - printk("arv: interrupted while get frame data.\n"); + printk(KERN_ERR "arv: interrupted while get frame data.\n"); ret = -EINTR; goto out_up; } @@ -334,7 +334,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) cpu_relax(); if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { for (h = 0; h < ar->height; h++) { - wait_for_vertical_sync(h); + wait_for_vertical_sync(ar, h); if (h < (AR_HEIGHT_VGA/2)) l = h << 1; else @@ -349,7 +349,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) } } else { for (h = 0; h < ar->height; h++) { - wait_for_vertical_sync(h); + wait_for_vertical_sync(ar, h); ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL); enable_dma(); while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) @@ -386,7 +386,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) } } if (copy_to_user(buf, yuv, ar->frame_bytes)) { - printk("arv: failed while copy_to_user yuv.\n"); + v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n"); ret = -EFAULT; goto out_up; } @@ -396,153 +396,127 @@ out_up: return ret; } -static long ar_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int ar_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - struct video_device *dev = video_devdata(file); - struct ar_device *ar = video_get_drvdata(dev); - - DEBUG(1, "ar_ioctl()\n"); - switch(cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - DEBUG(1, "VIDIOCGCAP:\n"); - strcpy(b->name, ar->vdev->name); - b->type = VID_TYPE_CAPTURE; - b->channels = 0; - b->audios = 0; - b->maxwidth = MAX_AR_WIDTH; - b->maxheight = MAX_AR_HEIGHT; - b->minwidth = MIN_AR_WIDTH; - b->minheight = MIN_AR_HEIGHT; - return 0; - } - case VIDIOCGCHAN: - DEBUG(1, "VIDIOCGCHAN:\n"); - return 0; - case VIDIOCSCHAN: - DEBUG(1, "VIDIOCSCHAN:\n"); - return 0; - case VIDIOCGTUNER: - DEBUG(1, "VIDIOCGTUNER:\n"); - return 0; - case VIDIOCSTUNER: - DEBUG(1, "VIDIOCSTUNER:\n"); - return 0; - case VIDIOCGPICT: - DEBUG(1, "VIDIOCGPICT:\n"); - return 0; - case VIDIOCSPICT: - DEBUG(1, "VIDIOCSPICT:\n"); - return 0; - case VIDIOCCAPTURE: - DEBUG(1, "VIDIOCCAPTURE:\n"); + struct ar *ar = video_drvdata(file); + + strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card)); + strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 0, 4); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} + +static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin) +{ + if (vin->index > 0) return -EINVAL; - case VIDIOCGWIN: - { - struct video_window *w = arg; - DEBUG(1, "VIDIOCGWIN:\n"); - memset(w, 0, sizeof(*w)); - w->width = ar->width; - w->height = ar->height; - return 0; + strlcpy(vin->name, "Camera", sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = V4L2_STD_ALL; + vin->status = 0; + return 0; +} + +static int ar_g_input(struct file *file, void *fh, unsigned int *inp) +{ + *inp = 0; + return 0; +} + +static int ar_s_input(struct file *file, void *fh, unsigned int inp) +{ + return inp ? -EINVAL : 0; +} + +static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ar *ar = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = ar->width; + pix->height = ar->height; + pix->pixelformat = V4L2_PIX_FMT_YUV422P; + pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED; + pix->bytesperline = ar->width; + pix->sizeimage = 2 * ar->width * ar->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ar *ar = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) { + pix->height = AR_HEIGHT_QVGA; + pix->width = AR_WIDTH_QVGA; + pix->field = V4L2_FIELD_INTERLACED; + } else { + pix->height = AR_HEIGHT_VGA; + pix->width = AR_WIDTH_VGA; + pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; } - case VIDIOCSWIN: - { - struct video_window *w = arg; - DEBUG(1, "VIDIOCSWIN:\n"); - if ((w->width != AR_WIDTH_VGA || w->height != AR_HEIGHT_VGA) && - (w->width != AR_WIDTH_QVGA || w->height != AR_HEIGHT_QVGA)) - return -EINVAL; - - mutex_lock(&ar->lock); - ar->width = w->width; - ar->height = w->height; - if (ar->width == AR_WIDTH_VGA) { - ar->size = AR_SIZE_VGA; - ar->frame_bytes = AR_FRAME_BYTES_VGA; - ar->line_bytes = AR_LINE_BYTES_VGA; - if (vga_interlace) - ar->mode = AR_MODE_INTERLACE; - else - ar->mode = AR_MODE_NORMAL; - } else { - ar->size = AR_SIZE_QVGA; - ar->frame_bytes = AR_FRAME_BYTES_QVGA; - ar->line_bytes = AR_LINE_BYTES_QVGA; + pix->pixelformat = V4L2_PIX_FMT_YUV422P; + pix->bytesperline = ar->width; + pix->sizeimage = 2 * ar->width * ar->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ar *ar = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = ar_try_fmt_vid_cap(file, fh, fmt); + + if (ret) + return ret; + mutex_lock(&ar->lock); + ar->width = pix->width; + ar->height = pix->height; + if (ar->width == AR_WIDTH_VGA) { + ar->size = AR_SIZE_VGA; + ar->frame_bytes = AR_FRAME_BYTES_VGA; + ar->line_bytes = AR_LINE_BYTES_VGA; + if (vga_interlace) ar->mode = AR_MODE_INTERLACE; - } - mutex_unlock(&ar->lock); - return 0; - } - case VIDIOCGFBUF: - DEBUG(1, "VIDIOCGFBUF:\n"); - return -EINVAL; - case VIDIOCSFBUF: - DEBUG(1, "VIDIOCSFBUF:\n"); - return -EINVAL; - case VIDIOCKEY: - DEBUG(1, "VIDIOCKEY:\n"); - return 0; - case VIDIOCGFREQ: - DEBUG(1, "VIDIOCGFREQ:\n"); - return -EINVAL; - case VIDIOCSFREQ: - DEBUG(1, "VIDIOCSFREQ:\n"); - return -EINVAL; - case VIDIOCGAUDIO: - DEBUG(1, "VIDIOCGAUDIO:\n"); - return -EINVAL; - case VIDIOCSAUDIO: - DEBUG(1, "VIDIOCSAUDIO:\n"); - return -EINVAL; - case VIDIOCSYNC: - DEBUG(1, "VIDIOCSYNC:\n"); - return -EINVAL; - case VIDIOCMCAPTURE: - DEBUG(1, "VIDIOCMCAPTURE:\n"); - return -EINVAL; - case VIDIOCGMBUF: - DEBUG(1, "VIDIOCGMBUF:\n"); - return -EINVAL; - case VIDIOCGUNIT: - DEBUG(1, "VIDIOCGUNIT:\n"); - return -EINVAL; - case VIDIOCGCAPTURE: - DEBUG(1, "VIDIOCGCAPTURE:\n"); - return -EINVAL; - case VIDIOCSCAPTURE: - DEBUG(1, "VIDIOCSCAPTURE:\n"); - return -EINVAL; - case VIDIOCSPLAYMODE: - DEBUG(1, "VIDIOCSPLAYMODE:\n"); - return -EINVAL; - case VIDIOCSWRITEMODE: - DEBUG(1, "VIDIOCSWRITEMODE:\n"); - return -EINVAL; - case VIDIOCGPLAYINFO: - DEBUG(1, "VIDIOCGPLAYINFO:\n"); - return -EINVAL; - case VIDIOCSMICROCODE: - DEBUG(1, "VIDIOCSMICROCODE:\n"); - return -EINVAL; - case VIDIOCGVBIFMT: - DEBUG(1, "VIDIOCGVBIFMT:\n"); - return -EINVAL; - case VIDIOCSVBIFMT: - DEBUG(1, "VIDIOCSVBIFMT:\n"); - return -EINVAL; - default: - DEBUG(1, "Unknown ioctl(0x%08x)\n", cmd); - return -ENOIOCTLCMD; + else + ar->mode = AR_MODE_NORMAL; + } else { + ar->size = AR_SIZE_QVGA; + ar->frame_bytes = AR_FRAME_BYTES_QVGA; + ar->line_bytes = AR_LINE_BYTES_QVGA; + ar->mode = AR_MODE_INTERLACE; } + /* Ok we figured out what to use from our wide choice */ + mutex_unlock(&ar->lock); return 0; } -static long ar_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - return video_usercopy(file, cmd, arg, ar_do_ioctl); + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 0) + return -EINVAL; + + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } #if USE_INT @@ -551,7 +525,7 @@ static long ar_ioctl(struct file *file, unsigned int cmd, */ static void ar_interrupt(int irq, void *dev) { - struct ar_device *ar = dev; + struct ar *ar = dev; unsigned int line_count; unsigned int line_number; unsigned int arvcr1; @@ -559,11 +533,11 @@ static void ar_interrupt(int irq, void *dev) line_count = ar_inl(ARVHCOUNT); /* line number */ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { /* operations for interlace mode */ - if ( line_count < (AR_HEIGHT_VGA/2) ) /* even line */ + if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */ line_number = (line_count << 1); else /* odd line */ line_number = - (((line_count - (AR_HEIGHT_VGA/2)) << 1) + 1); + (((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1); } else { line_number = line_count; } @@ -623,11 +597,10 @@ static void ar_interrupt(int irq, void *dev) * 0 is returned in success. * */ -static int ar_initialize(struct video_device *dev) +static int ar_initialize(struct ar *ar) { - struct ar_device *ar = video_get_drvdata(dev); unsigned long cr = 0; - int i,found=0; + int i, found = 0; DEBUG(1, "ar_initialize:\n"); @@ -666,129 +639,119 @@ static int ar_initialize(struct video_device *dev) if (found == 0) return -ENODEV; - printk("arv: Initializing "); - - iic(2,0x78,0x11,0x01,0x00); /* start */ - iic(3,0x78,0x12,0x00,0x06); - iic(3,0x78,0x12,0x12,0x30); - iic(3,0x78,0x12,0x15,0x58); - iic(3,0x78,0x12,0x17,0x30); - printk("."); - iic(3,0x78,0x12,0x1a,0x97); - iic(3,0x78,0x12,0x1b,0xff); - iic(3,0x78,0x12,0x1c,0xff); - iic(3,0x78,0x12,0x26,0x10); - iic(3,0x78,0x12,0x27,0x00); - printk("."); - iic(2,0x78,0x34,0x02,0x00); - iic(2,0x78,0x7a,0x10,0x00); - iic(2,0x78,0x80,0x39,0x00); - iic(2,0x78,0x81,0xe6,0x00); - iic(2,0x78,0x8d,0x00,0x00); - printk("."); - iic(2,0x78,0x8e,0x0c,0x00); - iic(2,0x78,0x8f,0x00,0x00); + v4l2_info(&ar->v4l2_dev, "Initializing "); + + iic(2, 0x78, 0x11, 0x01, 0x00); /* start */ + iic(3, 0x78, 0x12, 0x00, 0x06); + iic(3, 0x78, 0x12, 0x12, 0x30); + iic(3, 0x78, 0x12, 0x15, 0x58); + iic(3, 0x78, 0x12, 0x17, 0x30); + printk(KERN_CONT "."); + iic(3, 0x78, 0x12, 0x1a, 0x97); + iic(3, 0x78, 0x12, 0x1b, 0xff); + iic(3, 0x78, 0x12, 0x1c, 0xff); + iic(3, 0x78, 0x12, 0x26, 0x10); + iic(3, 0x78, 0x12, 0x27, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x34, 0x02, 0x00); + iic(2, 0x78, 0x7a, 0x10, 0x00); + iic(2, 0x78, 0x80, 0x39, 0x00); + iic(2, 0x78, 0x81, 0xe6, 0x00); + iic(2, 0x78, 0x8d, 0x00, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x8e, 0x0c, 0x00); + iic(2, 0x78, 0x8f, 0x00, 0x00); #if 0 - iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */ + iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */ #endif - iic(2,0x78,0x93,0x01,0x00); - iic(2,0x78,0x94,0xcd,0x00); - iic(2,0x78,0x95,0x00,0x00); - printk("."); - iic(2,0x78,0x96,0xa0,0x00); - iic(2,0x78,0x97,0x00,0x00); - iic(2,0x78,0x98,0x60,0x00); - iic(2,0x78,0x99,0x01,0x00); - iic(2,0x78,0x9a,0x19,0x00); - printk("."); - iic(2,0x78,0x9b,0x02,0x00); - iic(2,0x78,0x9c,0xe8,0x00); - iic(2,0x78,0x9d,0x02,0x00); - iic(2,0x78,0x9e,0x2e,0x00); - iic(2,0x78,0xb8,0x78,0x00); - iic(2,0x78,0xba,0x05,0x00); + iic(2, 0x78, 0x93, 0x01, 0x00); + iic(2, 0x78, 0x94, 0xcd, 0x00); + iic(2, 0x78, 0x95, 0x00, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x96, 0xa0, 0x00); + iic(2, 0x78, 0x97, 0x00, 0x00); + iic(2, 0x78, 0x98, 0x60, 0x00); + iic(2, 0x78, 0x99, 0x01, 0x00); + iic(2, 0x78, 0x9a, 0x19, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x9b, 0x02, 0x00); + iic(2, 0x78, 0x9c, 0xe8, 0x00); + iic(2, 0x78, 0x9d, 0x02, 0x00); + iic(2, 0x78, 0x9e, 0x2e, 0x00); + iic(2, 0x78, 0xb8, 0x78, 0x00); + iic(2, 0x78, 0xba, 0x05, 0x00); #if 0 - iic(2,0x78,0x83,0x8c,0x00); /* brightness */ + iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */ #endif - printk("."); + printk(KERN_CONT "."); /* color correction */ - iic(3,0x78,0x49,0x00,0x95); /* a */ - iic(3,0x78,0x49,0x01,0x96); /* b */ - iic(3,0x78,0x49,0x03,0x85); /* c */ - iic(3,0x78,0x49,0x04,0x97); /* d */ - iic(3,0x78,0x49,0x02,0x7e); /* e(Lo) */ - iic(3,0x78,0x49,0x05,0xa4); /* f(Lo) */ - iic(3,0x78,0x49,0x06,0x04); /* e(Hi) */ - iic(3,0x78,0x49,0x07,0x04); /* e(Hi) */ - iic(2,0x78,0x48,0x01,0x00); /* on=1 off=0 */ - - printk("."); - iic(2,0x78,0x11,0x00,0x00); /* end */ - printk(" done\n"); + iic(3, 0x78, 0x49, 0x00, 0x95); /* a */ + iic(3, 0x78, 0x49, 0x01, 0x96); /* b */ + iic(3, 0x78, 0x49, 0x03, 0x85); /* c */ + iic(3, 0x78, 0x49, 0x04, 0x97); /* d */ + iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */ + iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */ + iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */ + iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */ + iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */ + + printk(KERN_CONT "."); + iic(2, 0x78, 0x11, 0x00, 0x00); /* end */ + printk(KERN_CONT " done\n"); return 0; } -void ar_release(struct video_device *vfd) -{ - struct ar_device *ar = video_get_drvdata(vfd); - mutex_lock(&ar->lock); - video_device_release(vfd); -} - /**************************************************************************** * * Video4Linux Module functions * ****************************************************************************/ -static struct ar_device ardev; - -static int ar_exclusive_open(struct file *file) -{ - return test_and_set_bit(0, &ardev.in_use) ? -EBUSY : 0; -} - -static int ar_exclusive_release(struct file *file) -{ - clear_bit(0, &ardev.in_use); - return 0; -} static const struct v4l2_file_operations ar_fops = { .owner = THIS_MODULE, - .open = ar_exclusive_open, - .release = ar_exclusive_release, .read = ar_read, - .ioctl = ar_ioctl, + .ioctl = video_ioctl2, }; -static struct video_device ar_template = { - .name = "Colour AR VGA", - .fops = &ar_fops, - .release = ar_release, +static const struct v4l2_ioctl_ops ar_ioctl_ops = { + .vidioc_querycap = ar_querycap, + .vidioc_g_input = ar_g_input, + .vidioc_s_input = ar_s_input, + .vidioc_enum_input = ar_enum_input, + .vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap, }; #define ALIGN4(x) ((((int)(x)) & 0x3) == 0) static int __init ar_init(void) { - struct ar_device *ar; + struct ar *ar; + struct v4l2_device *v4l2_dev; int ret; int i; - DEBUG(1, "ar_init:\n"); - ret = -EIO; - printk(KERN_INFO "arv: Colour AR VGA driver %s\n", VERSION); - ar = &ardev; - memset(ar, 0, sizeof(struct ar_device)); + v4l2_dev = &ar->v4l2_dev; + strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name)); + v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION); + + ret = v4l2_device_register(NULL, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return ret; + } + ret = -EIO; #if USE_INT /* allocate a DMA buffer for 1 line. */ ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA); - if (ar->line_buff == NULL || ! ALIGN4(ar->line_buff)) { - printk("arv: buffer allocation failed for DMA.\n"); + if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) { + v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n"); ret = -ENOMEM; goto out_end; } @@ -796,20 +759,19 @@ static int __init ar_init(void) /* allocate buffers for a frame */ for (i = 0; i < MAX_AR_HEIGHT; i++) { ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL); - if (ar->frame[i] == NULL || ! ALIGN4(ar->frame[i])) { - printk("arv: buffer allocation failed for frame.\n"); + if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) { + v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n"); ret = -ENOMEM; goto out_line_buff; } } - ar->vdev = video_device_alloc(); - if (!ar->vdev) { - printk(KERN_ERR "arv: video_device_alloc() failed\n"); - return -ENOMEM; - } - memcpy(ar->vdev, &ar_template, sizeof(ar_template)); - video_set_drvdata(ar->vdev, ar); + strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name)); + ar->vdev.v4l2_dev = v4l2_dev; + ar->vdev.fops = &ar_fops; + ar->vdev.ioctl_ops = &ar_ioctl_ops; + ar->vdev.release = video_device_release_empty; + video_set_drvdata(&ar->vdev, ar); if (vga) { ar->width = AR_WIDTH_VGA; @@ -834,14 +796,14 @@ static int __init ar_init(void) #if USE_INT if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) { - printk("arv: request_irq(%d) failed.\n", M32R_IRQ_INT3); + v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3); ret = -EIO; goto out_irq; } #endif - if (ar_initialize(ar->vdev) != 0) { - printk("arv: M64278 not found.\n"); + if (ar_initialize(ar) != 0) { + v4l2_err(v4l2_dev, "M64278 not found.\n"); ret = -ENODEV; goto out_dev; } @@ -852,15 +814,15 @@ static int __init ar_init(void) * device is named "video[0-64]". * video_register_device() initializes h/w using ar_initialize(). */ - if (video_register_device(ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { + if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { /* return -1, -ENFILE(full) or others */ - printk("arv: register video (Colour AR) failed.\n"); + v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n"); ret = -ENODEV; goto out_dev; } - printk("%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", - video_device_node_name(ar->vdev), M32R_IRQ_INT3, freq); + v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", + video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq); return 0; @@ -879,6 +841,7 @@ out_line_buff: out_end: #endif + v4l2_device_unregister(&ar->v4l2_dev); return ret; } @@ -886,7 +849,7 @@ out_end: static int __init ar_init_module(void) { freq = (boot_cpu_data.bus_clock / 1000000); - printk("arv: Bus clock %d\n", freq); + printk(KERN_INFO "arv: Bus clock %d\n", freq); if (freq != 50 && freq != 75) freq = DEFAULT_FREQ; return ar_init(); @@ -894,11 +857,11 @@ static int __init ar_init_module(void) static void __exit ar_cleanup_module(void) { - struct ar_device *ar; + struct ar *ar; int i; ar = &ardev; - video_unregister_device(ar->vdev); + video_unregister_device(&ar->vdev); #if USE_INT free_irq(M32R_IRQ_INT3, ar); #endif @@ -907,6 +870,7 @@ static void __exit ar_cleanup_module(void) #if USE_INT kfree(ar->line_buff); #endif + v4l2_device_unregister(&ar->v4l2_dev); } module_init(ar_init_module); diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 8c140c01c5e..66150216f97 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -1105,7 +1105,7 @@ static int vidioc_enum_input(struct file *file, void *priv, tmp = input->index; - if (tmp > AU0828_MAX_INPUT) + if (tmp >= AU0828_MAX_INPUT) return -EINVAL; if (AUVI_INPUT(tmp).type == 0) return -EINVAL; diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 716870ae85d..7af56cde0c7 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -241,6 +241,10 @@ static struct CARD { { 0xa1550101, BTTV_BOARD_IVC200, "IVC-200G" }, { 0xa1550102, BTTV_BOARD_IVC200, "IVC-200G" }, { 0xa1550103, BTTV_BOARD_IVC200, "IVC-200G" }, + { 0xa1550800, BTTV_BOARD_IVC200, "IVC-200" }, + { 0xa1550801, BTTV_BOARD_IVC200, "IVC-200" }, + { 0xa1550802, BTTV_BOARD_IVC200, "IVC-200" }, + { 0xa1550803, BTTV_BOARD_IVC200, "IVC-200" }, { 0xa182ff00, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff01, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff02, BTTV_BOARD_IVC120, "IVC-120G" }, diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index f4860f03dfc..38c7f78ad9c 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1525,7 +1525,7 @@ static int bttv_s_ctrl(struct file *file, void *f, struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1806,8 +1806,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = gbuffers; - while (*size * *count > gbuffers * gbufsize) - (*count)--; + if (*size * *count > gbuffers * gbufsize) + *count = (gbuffers * gbufsize) / *size; return 0; } @@ -1859,7 +1859,7 @@ static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id) unsigned int i; int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1941,7 +1941,7 @@ static int bttv_s_input(struct file *file, void *priv, unsigned int i) int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1961,7 +1961,7 @@ static int bttv_s_tuner(struct file *file, void *priv, struct bttv *btv = fh->btv; int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1987,11 +1987,6 @@ static int bttv_g_frequency(struct file *file, void *priv, { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - int err; - - err = v4l2_prio_check(&btv->prio, &fh->prio); - if (0 != err) - return err; f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = btv->freq; @@ -2006,7 +2001,7 @@ static int bttv_s_frequency(struct file *file, void *priv, struct bttv *btv = fh->btv; int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -3029,7 +3024,7 @@ static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - retval = v4l2_prio_check(&btv->prio, &fh->prio); + retval = v4l2_prio_check(&btv->prio, fh->prio); if (0 != retval) return retval; @@ -3241,7 +3236,7 @@ static int bttv_open(struct file *file) *fh = btv->init; fh->type = type; fh->ov.setup_ok = 0; - v4l2_prio_open(&btv->prio,&fh->prio); + v4l2_prio_open(&btv->prio, &fh->prio); videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, &btv->c.pci->dev, &btv->s_lock, @@ -3312,7 +3307,7 @@ static int bttv_release(struct file *file) /* free stuff */ videobuf_mmap_free(&fh->cap); videobuf_mmap_free(&fh->vbi); - v4l2_prio_close(&btv->prio,&fh->prio); + v4l2_prio_close(&btv->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -3449,7 +3444,7 @@ static int radio_release(struct file *file) struct bttv *btv = fh->btv; struct rds_command cmd; - v4l2_prio_close(&btv->prio,&fh->prio); + v4l2_prio_close(&btv->prio, fh->prio); file->private_data = NULL; kfree(fh); diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index aa153a986ad..f68717a4bde 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -49,6 +49,8 @@ module_param(ir_rc5_key_timeout, int, 0644); #define DEVNAME "bttv-input" +#define MODULE_NAME "bttv" + /* ---------------------------------------------------------------------- */ static void ir_handle_key(struct bttv *btv) @@ -246,7 +248,7 @@ static void bttv_ir_stop(struct bttv *btv) int bttv_input_init(struct bttv *btv) { struct card_ir *ir; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; struct input_dev *input_dev; u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; @@ -264,7 +266,7 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_AVERMEDIA: case BTTV_BOARD_AVPHONE98: case BTTV_BOARD_AVERMEDIA98: - ir_codes = &ir_codes_avermedia_table; + ir_codes = RC_MAP_AVERMEDIA; ir->mask_keycode = 0xf88000; ir->mask_keydown = 0x010000; ir->polling = 50; // ms @@ -272,14 +274,14 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_AVDVBT_761: case BTTV_BOARD_AVDVBT_771: - ir_codes = &ir_codes_avermedia_dvbt_table; + ir_codes = RC_MAP_AVERMEDIA_DVBT; ir->mask_keycode = 0x0f00c0; ir->mask_keydown = 0x000020; ir->polling = 50; // ms break; case BTTV_BOARD_PXELVWPLTVPAK: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->mask_keycode = 0x003e00; ir->mask_keyup = 0x010000; ir->polling = 50; // ms @@ -287,24 +289,24 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_PV_M4900: case BTTV_BOARD_PV_BT878P_9B: case BTTV_BOARD_PV_BT878P_PLUS: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; case BTTV_BOARD_WINFAST2000: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->mask_keycode = 0x1f8; break; case BTTV_BOARD_MAGICTVIEW061: case BTTV_BOARD_MAGICTVIEW063: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->mask_keycode = 0x0008e000; ir->mask_keydown = 0x00200000; break; case BTTV_BOARD_APAC_VIEWCOMP: - ir_codes = &ir_codes_apac_viewcomp_table; + ir_codes = RC_MAP_APAC_VIEWCOMP; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms @@ -312,30 +314,30 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_ASKEY_CPH03X: case BTTV_BOARD_CONCEPTRONIC_CTVFMI2: case BTTV_BOARD_CONTVFMI: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x006000; ir->polling = 50; // ms break; case BTTV_BOARD_NEBULA_DIGITV: - ir_codes = &ir_codes_nebula_table; + ir_codes = RC_MAP_NEBULA; btv->custom_irq = bttv_rc5_irq; ir->rc5_gpio = 1; break; case BTTV_BOARD_MACHTV_MAGICTV: - ir_codes = &ir_codes_apac_viewcomp_table; + ir_codes = RC_MAP_APAC_VIEWCOMP; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x004000; ir->polling = 50; /* ms */ break; case BTTV_BOARD_KOZUMI_KTV_01C: - ir_codes = &ir_codes_pctv_sedna_table; + ir_codes = RC_MAP_PCTV_SEDNA; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x006000; ir->polling = 50; /* ms */ break; case BTTV_BOARD_ENLTV_FM_2: - ir_codes = &ir_codes_encore_enltv2_table; + ir_codes = RC_MAP_ENCORE_ENLTV2; ir->mask_keycode = 0x00fd00; ir->mask_keyup = 0x000080; ir->polling = 1; /* ms */ @@ -390,7 +392,7 @@ int bttv_input_init(struct bttv *btv) bttv_ir_start(btv, ir); /* all done */ - err = ir_input_register(btv->remote->dev, ir_codes, NULL); + err = ir_input_register(btv->remote->dev, ir_codes, NULL, MODULE_NAME); if (err) goto err_out_stop; diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 9e39bc5f7b0..3c9e754d73a 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -80,8 +80,8 @@ OTHER DEALINGS IN THE SOFTWARE. #include "bw-qcam.h" -static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ -static unsigned int yieldlines=4; /* Yield after this many during capture */ +static unsigned int maxpoll = 250; /* Maximum busy-loop count for qcam I/O */ +static unsigned int yieldlines = 4; /* Yield after this many during capture */ static int video_nr = -1; static unsigned int force_init; /* Whether to probe aggressively */ @@ -156,7 +156,7 @@ static int qc_calibrate(struct qcam_device *q) mdelay(1); schedule(); count++; - } while (value == 0xff && count<2048); + } while (value == 0xff && count < 2048); q->whitebal = value; return value; @@ -170,16 +170,15 @@ static struct qcam_device *qcam_init(struct parport *port) struct qcam_device *q; q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); - if(q==NULL) + if (q == NULL) return NULL; q->pport = port; q->pdev = parport_register_device(port, "bw-qcam", NULL, NULL, - NULL, 0, NULL); - if (q->pdev == NULL) - { + NULL, 0, NULL); + if (q->pdev == NULL) { printk(KERN_ERR "bw-qcam: couldn't register for %s.\n", - port->name); + port->name); kfree(q); return NULL; } @@ -247,12 +246,10 @@ static int qc_readparam(struct qcam_device *q) static int qc_waithand(struct qcam_device *q, int val) { int status; - int runs=0; + int runs = 0; - if (val) - { - while (!((status = read_lpstatus(q)) & 8)) - { + if (val) { + while (!((status = read_lpstatus(q)) & 8)) { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly until the camera wakes up. However, we are @@ -260,18 +257,13 @@ static int qc_waithand(struct qcam_device *q, int val) setting it lower is much better for interactive response. */ - if(runs++>maxpoll) - { + if (runs++ > maxpoll) msleep_interruptible(5); - } - if(runs>(maxpoll+1000)) /* 5 seconds */ + if (runs > (maxpoll + 1000)) /* 5 seconds */ return -1; } - } - else - { - while (((status = read_lpstatus(q)) & 8)) - { + } else { + while (((status = read_lpstatus(q)) & 8)) { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly until the camera wakes up. However, we are @@ -279,11 +271,9 @@ static int qc_waithand(struct qcam_device *q, int val) setting it lower is much better for interactive response. */ - if(runs++>maxpoll) - { + if (runs++ > maxpoll) msleep_interruptible(5); - } - if(runs++>(maxpoll+1000)) /* 5 seconds */ + if (runs++ > (maxpoll + 1000)) /* 5 seconds */ return -1; } } @@ -299,10 +289,9 @@ static int qc_waithand(struct qcam_device *q, int val) static unsigned int qc_waithand2(struct qcam_device *q, int val) { unsigned int status; - int runs=0; + int runs = 0; - do - { + do { status = read_lpdata(q); /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly @@ -311,14 +300,11 @@ static unsigned int qc_waithand2(struct qcam_device *q, int val) setting it lower is much better for interactive response. */ - if(runs++>maxpoll) - { + if (runs++ > maxpoll) msleep_interruptible(5); - } - if(runs++>(maxpoll+1000)) /* 5 seconds */ + if (runs++ > (maxpoll + 1000)) /* 5 seconds */ return 0; - } - while ((status & 1) != val); + } while ((status & 1) != val); return status; } @@ -342,8 +328,7 @@ static int qc_detect(struct qcam_device *q) lastreg = reg = read_lpstatus(q) & 0xf0; - for (i = 0; i < 500; i++) - { + for (i = 0; i < 500; i++) { reg = read_lpstatus(q) & 0xf0; if (reg != lastreg) count++; @@ -357,7 +342,7 @@ static int qc_detect(struct qcam_device *q) won't be flashing these bits. Possibly unloading the module in the middle of a grab? Or some timeout condition? I've seen this parameter as low as 19 on my 450Mhz box - mpc */ - printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); + printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); return 1; #endif @@ -367,7 +352,7 @@ static int qc_detect(struct qcam_device *q) return 1; /* found */ } else { printk(KERN_ERR "No Quickcam found on port %s\n", - q->pport->name); + q->pport->name); printk(KERN_DEBUG "Quickcam detection counter: %u\n", count); return 0; /* not found */ } @@ -381,26 +366,24 @@ static int qc_detect(struct qcam_device *q) static void qc_reset(struct qcam_device *q) { - switch (q->port_mode & QC_FORCE_MASK) - { - case QC_FORCE_UNIDIR: - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; - break; + switch (q->port_mode & QC_FORCE_MASK) { + case QC_FORCE_UNIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + break; - case QC_FORCE_BIDIR: - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; - break; + case QC_FORCE_BIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + break; - case QC_ANY: - write_lpcontrol(q, 0x20); - write_lpdata(q, 0x75); + case QC_ANY: + write_lpcontrol(q, 0x20); + write_lpdata(q, 0x75); - if (read_lpdata(q) != 0x75) { - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; - } else { - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; - } - break; + if (read_lpdata(q) != 0x75) + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + else + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + break; } write_lpcontrol(q, 0xb); @@ -423,36 +406,33 @@ static int qc_setscanmode(struct qcam_device *q) { int old_mode = q->mode; - switch (q->transfer_scale) - { - case 1: - q->mode = 0; - break; - case 2: - q->mode = 4; - break; - case 4: - q->mode = 8; - break; + switch (q->transfer_scale) { + case 1: + q->mode = 0; + break; + case 2: + q->mode = 4; + break; + case 4: + q->mode = 8; + break; } - switch (q->bpp) - { - case 4: - break; - case 6: - q->mode += 2; - break; + switch (q->bpp) { + case 4: + break; + case 6: + q->mode += 2; + break; } - switch (q->port_mode & QC_MODE_MASK) - { - case QC_BIDIR: - q->mode += 1; - break; - case QC_NOTSET: - case QC_UNIDIR: - break; + switch (q->port_mode & QC_MODE_MASK) { + case QC_BIDIR: + q->mode += 1; + break; + case QC_NOTSET: + case QC_UNIDIR: + break; } if (q->mode != old_mode) @@ -493,7 +473,7 @@ static void qc_set(struct qcam_device *q) } else { val = q->width * q->bpp; val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * - q->transfer_scale; + q->transfer_scale; } val = DIV_ROUND_UP(val, val2); qc_command(q, 0x13); @@ -521,85 +501,80 @@ static void qc_set(struct qcam_device *q) static inline int qc_readbytes(struct qcam_device *q, char buffer[]) { - int ret=1; + int ret = 1; unsigned int hi, lo; unsigned int hi2, lo2; static int state; - if (buffer == NULL) - { + if (buffer == NULL) { state = 0; return 0; } - switch (q->port_mode & QC_MODE_MASK) - { - case QC_BIDIR: /* Bi-directional Port */ - write_lpcontrol(q, 0x26); - lo = (qc_waithand2(q, 1) >> 1); - hi = (read_lpstatus(q) >> 3) & 0x1f; - write_lpcontrol(q, 0x2e); - lo2 = (qc_waithand2(q, 0) >> 1); - hi2 = (read_lpstatus(q) >> 3) & 0x1f; - switch (q->bpp) - { - case 4: - buffer[0] = lo & 0xf; - buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); - buffer[2] = (hi & 0x1e) >> 1; - buffer[3] = lo2 & 0xf; - buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); - buffer[5] = (hi2 & 0x1e) >> 1; - ret = 6; - break; - case 6: - buffer[0] = lo & 0x3f; - buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); - buffer[2] = lo2 & 0x3f; - buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); - ret = 4; - break; - } + switch (q->port_mode & QC_MODE_MASK) { + case QC_BIDIR: /* Bi-directional Port */ + write_lpcontrol(q, 0x26); + lo = (qc_waithand2(q, 1) >> 1); + hi = (read_lpstatus(q) >> 3) & 0x1f; + write_lpcontrol(q, 0x2e); + lo2 = (qc_waithand2(q, 0) >> 1); + hi2 = (read_lpstatus(q) >> 3) & 0x1f; + switch (q->bpp) { + case 4: + buffer[0] = lo & 0xf; + buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); + buffer[2] = (hi & 0x1e) >> 1; + buffer[3] = lo2 & 0xf; + buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); + buffer[5] = (hi2 & 0x1e) >> 1; + ret = 6; + break; + case 6: + buffer[0] = lo & 0x3f; + buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); + buffer[2] = lo2 & 0x3f; + buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); + ret = 4; break; + } + break; + + case QC_UNIDIR: /* Unidirectional Port */ + write_lpcontrol(q, 6); + lo = (qc_waithand(q, 1) & 0xf0) >> 4; + write_lpcontrol(q, 0xe); + hi = (qc_waithand(q, 0) & 0xf0) >> 4; - case QC_UNIDIR: /* Unidirectional Port */ - write_lpcontrol(q, 6); - lo = (qc_waithand(q, 1) & 0xf0) >> 4; - write_lpcontrol(q, 0xe); - hi = (qc_waithand(q, 0) & 0xf0) >> 4; - - switch (q->bpp) - { - case 4: - buffer[0] = lo; - buffer[1] = hi; - ret = 2; - break; - case 6: - switch (state) - { - case 0: - buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); - q->saved_bits = (hi & 3) << 4; - state = 1; - ret = 1; - break; - case 1: - buffer[0] = lo | q->saved_bits; - q->saved_bits = hi << 2; - state = 2; - ret = 1; - break; - case 2: - buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; - buffer[1] = ((lo & 3) << 4) | hi; - state = 0; - ret = 2; - break; - } - break; + switch (q->bpp) { + case 4: + buffer[0] = lo; + buffer[1] = hi; + ret = 2; + break; + case 6: + switch (state) { + case 0: + buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); + q->saved_bits = (hi & 3) << 4; + state = 1; + ret = 1; + break; + case 1: + buffer[0] = lo | q->saved_bits; + q->saved_bits = hi << 2; + state = 2; + ret = 1; + break; + case 2: + buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; + buffer[1] = ((lo & 3) << 4) | hi; + state = 0; + ret = 2; + break; } break; + } + break; } return ret; } @@ -615,7 +590,7 @@ static inline int qc_readbytes(struct qcam_device *q, char buffer[]) * n=2^(bit depth)-1. Ask me for more details if you don't understand * this. */ -static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long len) +static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long len) { int i, j, k, yield; int bytes; @@ -623,9 +598,9 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l int divisor; int pixels_per_line; int pixels_read = 0; - int got=0; + int got = 0; char buffer[6]; - int shift=8-q->bpp; + int shift = 8 - q->bpp; char invert; if (q->mode == -1) @@ -634,13 +609,12 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l qc_command(q, 0x7); qc_command(q, q->mode); - if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) - { + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { write_lpcontrol(q, 0x2e); /* turn port around */ write_lpcontrol(q, 0x26); - (void) qc_waithand(q, 1); + qc_waithand(q, 1); write_lpcontrol(q, 0x2e); - (void) qc_waithand(q, 0); + qc_waithand(q, 0); } /* strange -- should be 15:63 below, but 4bpp is odd */ @@ -650,33 +624,28 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l pixels_per_line = q->width / q->transfer_scale; transperline = q->width * q->bpp; divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * - q->transfer_scale; + q->transfer_scale; transperline = DIV_ROUND_UP(transperline, divisor); - for (i = 0, yield = yieldlines; i < linestotrans; i++) - { - for (pixels_read = j = 0; j < transperline; j++) - { + for (i = 0, yield = yieldlines; i < linestotrans; i++) { + for (pixels_read = j = 0; j < transperline; j++) { bytes = qc_readbytes(q, buffer); - for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) - { + for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) { int o; - if (buffer[k] == 0 && invert == 16) - { + if (buffer[k] == 0 && invert == 16) { /* 4bpp is odd (again) -- inverter is 16, not 15, but output must be 0-15 -- bls */ buffer[k] = 16; } - o=i*pixels_per_line + pixels_read + k; - if(o<len) - { + o = i * pixels_per_line + pixels_read + k; + if (o < len) { got++; - put_user((invert - buffer[k])<<shift, buf+o); + put_user((invert - buffer[k]) << shift, buf + o); } } pixels_read += bytes; } - (void) qc_readbytes(q, NULL); /* reset state machine */ + qc_readbytes(q, NULL); /* reset state machine */ /* Grabbing an entire frame from the quickcam is a lengthy process. We don't (usually) want to busy-block the @@ -690,14 +659,13 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l } } - if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) - { + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { write_lpcontrol(q, 2); write_lpcontrol(q, 6); udelay(3); write_lpcontrol(q, 0xe); } - if(got<len) + if (got < len) return got; return len; } @@ -709,11 +677,10 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)dev; + struct qcam_device *qcam = (struct qcam_device *)dev; - switch(cmd) - { - case VIDIOCGCAP: + switch (cmd) { + case VIDIOCGCAP: { struct video_capability *b = arg; strcpy(b->name, "Quickcam"); @@ -726,73 +693,73 @@ static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) b->minheight = 60; return 0; } - case VIDIOCGCHAN: + case VIDIOCGCHAN: { struct video_channel *v = arg; - if(v->channel!=0) + if (v->channel != 0) return -EINVAL; - v->flags=0; - v->tuners=0; + v->flags = 0; + v->tuners = 0; /* Good question.. its composite or SVHS so.. */ v->type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Camera"); return 0; } - case VIDIOCSCHAN: + case VIDIOCSCHAN: { struct video_channel *v = arg; - if(v->channel!=0) + if (v->channel != 0) return -EINVAL; return 0; } - case VIDIOCGTUNER: + case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) + if (v->tuner) return -EINVAL; strcpy(v->name, "Format"); - v->rangelow=0; - v->rangehigh=0; - v->flags= 0; + v->rangelow = 0; + v->rangehigh = 0; + v->flags = 0; v->mode = VIDEO_MODE_AUTO; return 0; } - case VIDIOCSTUNER: + case VIDIOCSTUNER: { struct video_tuner *v = arg; - if(v->tuner) + if (v->tuner) return -EINVAL; - if(v->mode!=VIDEO_MODE_AUTO) + if (v->mode != VIDEO_MODE_AUTO) return -EINVAL; return 0; } - case VIDIOCGPICT: + case VIDIOCGPICT: { struct video_picture *p = arg; - p->colour=0x8000; - p->hue=0x8000; - p->brightness=qcam->brightness<<8; - p->contrast=qcam->contrast<<8; - p->whiteness=qcam->whitebal<<8; - p->depth=qcam->bpp; - p->palette=VIDEO_PALETTE_GREY; + p->colour = 0x8000; + p->hue = 0x8000; + p->brightness = qcam->brightness << 8; + p->contrast = qcam->contrast << 8; + p->whiteness = qcam->whitebal << 8; + p->depth = qcam->bpp; + p->palette = VIDEO_PALETTE_GREY; return 0; } - case VIDIOCSPICT: + case VIDIOCSPICT: { struct video_picture *p = arg; - if(p->palette!=VIDEO_PALETTE_GREY) + if (p->palette != VIDEO_PALETTE_GREY) return -EINVAL; - if(p->depth!=4 && p->depth!=6) + if (p->depth != 4 && p->depth != 6) return -EINVAL; /* * Now load the camera. */ - qcam->brightness = p->brightness>>8; - qcam->contrast = p->contrast>>8; - qcam->whitebal = p->whiteness>>8; + qcam->brightness = p->brightness >> 8; + qcam->contrast = p->contrast >> 8; + qcam->whitebal = p->whiteness >> 8; qcam->bpp = p->depth; mutex_lock(&qcam->lock); @@ -802,28 +769,25 @@ static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) return 0; } - case VIDIOCSWIN: + case VIDIOCSWIN: { struct video_window *vw = arg; - if(vw->flags) + if (vw->flags) return -EINVAL; - if(vw->clipcount) + if (vw->clipcount) return -EINVAL; - if(vw->height<60||vw->height>240) + if (vw->height < 60 || vw->height > 240) return -EINVAL; - if(vw->width<80||vw->width>320) + if (vw->width < 80 || vw->width > 320) return -EINVAL; qcam->width = 320; qcam->height = 240; qcam->transfer_scale = 4; - if(vw->width>=160 && vw->height>=120) - { + if (vw->width >= 160 && vw->height >= 120) qcam->transfer_scale = 2; - } - if(vw->width>=320 && vw->height>=240) - { + if (vw->width >= 320 && vw->height >= 240) { qcam->width = 320; qcam->height = 240; qcam->transfer_scale = 1; @@ -839,41 +803,42 @@ static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Ok we figured out what to use from our wide choice */ return 0; } - case VIDIOCGWIN: + case VIDIOCGWIN: { struct video_window *vw = arg; + memset(vw, 0, sizeof(*vw)); - vw->width=qcam->width/qcam->transfer_scale; - vw->height=qcam->height/qcam->transfer_scale; + vw->width = qcam->width / qcam->transfer_scale; + vw->height = qcam->height / qcam->transfer_scale; return 0; } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; } return 0; } static long qcam_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { return video_usercopy(file, cmd, arg, qcam_do_ioctl); } static ssize_t qcam_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { struct video_device *v = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)v; + struct qcam_device *qcam = (struct qcam_device *)v; int len; parport_claim_or_block(qcam->pdev); @@ -885,7 +850,7 @@ static ssize_t qcam_read(struct file *file, char __user *buf, if (qcam->status & QC_PARAM_CHANGE) qc_set(qcam); - len=qc_capture(qcam, buf,count); + len = qc_capture(qcam, buf, count); mutex_unlock(&qcam->lock); @@ -917,8 +882,7 @@ static const struct v4l2_file_operations qcam_fops = { .ioctl = qcam_ioctl, .read = qcam_read, }; -static struct video_device qcam_template= -{ +static struct video_device qcam_template = { .name = "Connectix Quickcam", .fops = &qcam_fops, .release = video_device_release_empty, @@ -932,22 +896,20 @@ static int init_bwqcam(struct parport *port) { struct qcam_device *qcam; - if (num_cams == MAX_CAMS) - { + if (num_cams == MAX_CAMS) { printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); return -ENOSPC; } - qcam=qcam_init(port); - if(qcam==NULL) + qcam = qcam_init(port); + if (qcam == NULL) return -ENODEV; parport_claim_or_block(qcam->pdev); qc_reset(qcam); - if(qc_detect(qcam)==0) - { + if (qc_detect(qcam) == 0) { parport_release(qcam->pdev); parport_unregister_device(qcam->pdev); kfree(qcam); @@ -1045,12 +1007,12 @@ static int __init init_bw_qcams(void) #ifdef MODULE /* Do some sanity checks on the module parameters. */ if (maxpoll > 5000) { - printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n"); + printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n"); maxpoll = 5000; } if (yieldlines < 1) { - printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n"); + printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n"); yieldlines = 1; } #endif diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index e2cbebab959..8f1dd88b32a 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -79,17 +79,17 @@ static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i) { /* note: the QC specs refer to the PCAck pin by voltage, not software level. PC ports have builtin inverters. */ - parport_frob_control(qcam->pport, 8, i?8:0); + parport_frob_control(qcam->pport, 8, i ? 8 : 0); } static inline unsigned int qcam_ready1(struct qcam_device *qcam) { - return (parport_read_status(qcam->pport) & 0x8)?1:0; + return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0; } static inline unsigned int qcam_ready2(struct qcam_device *qcam) { - return (parport_read_data(qcam->pport) & 0x1)?1:0; + return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0; } static unsigned int qcam_await_ready1(struct qcam_device *qcam, @@ -99,14 +99,13 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam, unsigned int i; for (oldjiffies = jiffies; - time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) + time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) if (qcam_ready1(qcam) == value) return 0; /* If the camera didn't respond within 1/25 second, poll slowly for a while. */ - for (i = 0; i < 50; i++) - { + for (i = 0; i < 50; i++) { if (qcam_ready1(qcam) == value) return 0; msleep_interruptible(100); @@ -125,14 +124,13 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) unsigned int i; for (oldjiffies = jiffies; - time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) + time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) if (qcam_ready2(qcam) == value) return 0; /* If the camera didn't respond within 1/25 second, poll slowly for a while. */ - for (i = 0; i < 50; i++) - { + for (i = 0; i < 50; i++) { if (qcam_ready2(qcam) == value) return 0; msleep_interruptible(100); @@ -149,22 +147,25 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) static int qcam_read_data(struct qcam_device *qcam) { unsigned int idata; + qcam_set_ack(qcam, 0); - if (qcam_await_ready1(qcam, 1)) return -1; + if (qcam_await_ready1(qcam, 1)) + return -1; idata = parport_read_status(qcam->pport) & 0xf0; qcam_set_ack(qcam, 1); - if (qcam_await_ready1(qcam, 0)) return -1; - idata |= (parport_read_status(qcam->pport) >> 4); + if (qcam_await_ready1(qcam, 0)) + return -1; + idata |= parport_read_status(qcam->pport) >> 4; return idata; } static int qcam_write_data(struct qcam_device *qcam, unsigned int data) { unsigned int idata; + parport_write_data(qcam->pport, data); idata = qcam_read_data(qcam); - if (data != idata) - { + if (data != idata) { printk(KERN_WARNING "cqcam: sent %x but received %x\n", data, idata); return 1; @@ -212,13 +213,12 @@ static int qc_detect(struct qcam_device *qcam) /* look for a heartbeat */ ostat = stat = parport_read_status(qcam->pport); - for (i=0; i<250; i++) - { + for (i = 0; i < 250; i++) { mdelay(1); stat = parport_read_status(qcam->pport); - if (ostat != stat) - { - if (++count >= 3) return 1; + if (ostat != stat) { + if (++count >= 3) + return 1; ostat = stat; } } @@ -232,13 +232,12 @@ static int qc_detect(struct qcam_device *qcam) count = 0; ostat = stat = parport_read_status(qcam->pport); - for (i=0; i<250; i++) - { + for (i = 0; i < 250; i++) { mdelay(1); stat = parport_read_status(qcam->pport); - if (ostat != stat) - { - if (++count >= 3) return 1; + if (ostat != stat) { + if (++count >= 3) + return 1; ostat = stat; } } @@ -263,7 +262,7 @@ static void qc_setup(struct qcam_device *q) { qc_reset(q); - /* Set the brightness. */ + /* Set the brightness. */ qcam_set(q, 11, q->brightness); /* Set the height and width. These refer to the actual @@ -292,25 +291,25 @@ static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, u unsigned int bytes = 0; qcam_set_ack(q, 0); - if (q->bidirectional) - { + if (q->bidirectional) { /* It's a bidirectional port */ - while (bytes < nbytes) - { + while (bytes < nbytes) { unsigned int lo1, hi1, lo2, hi2; unsigned char r, g, b; - if (qcam_await_ready2(q, 1)) return bytes; + if (qcam_await_ready2(q, 1)) + return bytes; lo1 = parport_read_data(q->pport) >> 1; hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; qcam_set_ack(q, 1); - if (qcam_await_ready2(q, 0)) return bytes; + if (qcam_await_ready2(q, 0)) + return bytes; lo2 = parport_read_data(q->pport) >> 1; hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; qcam_set_ack(q, 0); - r = (lo1 | ((hi1 & 1)<<7)); - g = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1); - b = (lo2 | ((hi2 & 1)<<7)); + r = lo1 | ((hi1 & 1) << 7); + g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1); + b = lo2 | ((hi2 & 1) << 7); if (force_rgb) { buf[bytes++] = r; buf[bytes++] = g; @@ -321,21 +320,20 @@ static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, u buf[bytes++] = r; } } - } - else - { + } else { /* It's a unidirectional port */ int i = 0, n = bytes; unsigned char rgb[3]; - while (bytes < nbytes) - { + while (bytes < nbytes) { unsigned int hi, lo; - if (qcam_await_ready1(q, 1)) return bytes; + if (qcam_await_ready1(q, 1)) + return bytes; hi = (parport_read_status(q->pport) & 0xf0); qcam_set_ack(q, 1); - if (qcam_await_ready1(q, 0)) return bytes; + if (qcam_await_ready1(q, 0)) + return bytes; lo = (parport_read_status(q->pport) & 0xf0); qcam_set_ack(q, 0); /* flip some bits */ @@ -374,28 +372,26 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le return -EFAULT; /* Wait for camera to become ready */ - for (;;) - { + for (;;) { int i = qcam_get(q, 41); + if (i == -1) { qc_setup(q); return -EIO; } if ((i & 0x80) == 0) break; - else - schedule(); + schedule(); } - if (qcam_set(q, 7, (q->mode | (is_bi_dir?1:0)) + 1)) + if (qcam_set(q, 7, (q->mode | (is_bi_dir ? 1 : 0)) + 1)) return -EIO; lines = q->height; pixelsperline = q->width; bitsperxfer = (is_bi_dir) ? 24 : 8; - if (is_bi_dir) - { + if (is_bi_dir) { /* Turn the port around */ parport_data_reverse(q->pport); mdelay(3); @@ -413,16 +409,17 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le wantlen = lines * pixelsperline * 24 / 8; - while (wantlen) - { + while (wantlen) { size_t t, s; - s = (wantlen > BUFSZ)?BUFSZ:wantlen; + + s = (wantlen > BUFSZ) ? BUFSZ : wantlen; t = qcam_read_bytes(q, tmpbuf, s); - if (outptr < len) - { + if (outptr < len) { size_t sz = len - outptr; - if (sz > t) sz = t; - if (__copy_to_user(buf+outptr, tmpbuf, sz)) + + if (sz > t) + sz = t; + if (__copy_to_user(buf + outptr, tmpbuf, sz)) break; outptr += sz; } @@ -434,33 +431,31 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le len = outptr; - if (wantlen) - { - printk("qcam: short read.\n"); + if (wantlen) { + printk(KERN_ERR "qcam: short read.\n"); if (is_bi_dir) parport_data_forward(q->pport); qc_setup(q); return len; } - if (is_bi_dir) - { + if (is_bi_dir) { int l; + do { l = qcam_read_bytes(q, tmpbuf, 3); cond_resched(); } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); if (force_rgb) { if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } else { if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } qcam_set_ack(q, 0); - if (qcam_await_ready1(q, 1)) - { - printk("qcam: no ack after EOF\n"); + if (qcam_await_ready1(q, 1)) { + printk(KERN_ERR "qcam: no ack after EOF\n"); parport_data_forward(q->pport); qc_setup(q); return len; @@ -468,27 +463,25 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le parport_data_forward(q->pport); mdelay(3); qcam_set_ack(q, 1); - if (qcam_await_ready1(q, 0)) - { - printk("qcam: no ack to port turnaround\n"); + if (qcam_await_ready1(q, 0)) { + printk(KERN_ERR "qcam: no ack to port turnaround\n"); qc_setup(q); return len; } - } - else - { + } else { int l; + do { l = qcam_read_bytes(q, tmpbuf, 1); cond_resched(); } while (l && tmpbuf[0] == 0x7e); - l = qcam_read_bytes(q, tmpbuf+1, 2); + l = qcam_read_bytes(q, tmpbuf + 1, 2); if (force_rgb) { if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } else { if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } } @@ -503,164 +496,166 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)dev; + struct qcam_device *qcam = (struct qcam_device *)dev; - switch(cmd) + switch (cmd) { + case VIDIOCGCAP: { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, "Quickcam"); - b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; - b->channels = 1; - b->audios = 0; - b->maxwidth = 320; - b->maxheight = 240; - b->minwidth = 80; - b->minheight = 60; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - if(v->channel!=0) - return -EINVAL; - v->flags=0; - v->tuners=0; - /* Good question.. its composite or SVHS so.. */ - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if(v->channel!=0) - return -EINVAL; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - memset(v,0,sizeof(*v)); - strcpy(v->name, "Format"); - v->mode = VIDEO_MODE_AUTO; - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - if(v->mode!=VIDEO_MODE_AUTO) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - p->colour=0x8000; - p->hue=0x8000; - p->brightness=qcam->brightness<<8; - p->contrast=qcam->contrast<<8; - p->whiteness=qcam->whitebal<<8; - p->depth=24; - p->palette=VIDEO_PALETTE_RGB24; - return 0; + struct video_capability *b = arg; + + strcpy(b->name, "Quickcam"); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + b->channels = 1; + b->audios = 0; + b->maxwidth = 320; + b->maxheight = 240; + b->minwidth = 80; + b->minheight = 60; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + /* Good question.. its composite or SVHS so.. */ + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Camera"); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + + if (v->tuner) + return -EINVAL; + memset(v, 0, sizeof(*v)); + strcpy(v->name, "Format"); + v->mode = VIDEO_MODE_AUTO; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + + if (v->tuner) + return -EINVAL; + if (v->mode != VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + + p->colour = 0x8000; + p->hue = 0x8000; + p->brightness = qcam->brightness << 8; + p->contrast = qcam->contrast << 8; + p->whiteness = qcam->whitebal << 8; + p->depth = 24; + p->palette = VIDEO_PALETTE_RGB24; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + + /* + * Sanity check args + */ + if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24) + return -EINVAL; + + /* + * Now load the camera. + */ + qcam->brightness = p->brightness >> 8; + qcam->contrast = p->contrast >> 8; + qcam->whitebal = p->whiteness >> 8; + + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->height < 60 || vw->height > 240) + return -EINVAL; + if (vw->width < 80 || vw->width > 320) + return -EINVAL; + + qcam->width = 80; + qcam->height = 60; + qcam->mode = QC_DECIMATION_4; + + if (vw->width >= 160 && vw->height >= 120) { + qcam->width = 160; + qcam->height = 120; + qcam->mode = QC_DECIMATION_2; } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - - /* - * Sanity check args - */ - if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24) - return -EINVAL; - - /* - * Now load the camera. - */ - qcam->brightness = p->brightness>>8; - qcam->contrast = p->contrast>>8; - qcam->whitebal = p->whiteness>>8; - - mutex_lock(&qcam->lock); - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - return 0; + if (vw->width >= 320 && vw->height >= 240) { + qcam->width = 320; + qcam->height = 240; + qcam->mode = QC_DECIMATION_1; } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if(vw->flags) - return -EINVAL; - if(vw->clipcount) - return -EINVAL; - if(vw->height<60||vw->height>240) - return -EINVAL; - if(vw->width<80||vw->width>320) - return -EINVAL; - - qcam->width = 80; - qcam->height = 60; - qcam->mode = QC_DECIMATION_4; - - if(vw->width>=160 && vw->height>=120) - { - qcam->width = 160; - qcam->height = 120; - qcam->mode = QC_DECIMATION_2; - } - if(vw->width>=320 && vw->height>=240) - { - qcam->width = 320; - qcam->height = 240; - qcam->mode = QC_DECIMATION_1; - } - qcam->mode |= QC_MILLIONS; + qcam->mode |= QC_MILLIONS; #if 0 - if(vw->width>=640 && vw->height>=480) - { - qcam->width = 640; - qcam->height = 480; - qcam->mode = QC_BILLIONS | QC_DECIMATION_1; - } -#endif - /* Ok we figured out what to use from our - wide choice */ - mutex_lock(&qcam->lock); - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - memset(vw, 0, sizeof(*vw)); - vw->width=qcam->width; - vw->height=qcam->height; - return 0; + if (vw->width >= 640 && vw->height >= 480) { + qcam->width = 640; + qcam->height = 480; + qcam->mode = QC_BILLIONS | QC_DECIMATION_1; } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; +#endif + /* Ok we figured out what to use from our + wide choice */ + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + memset(vw, 0, sizeof(*vw)); + vw->width = qcam->width; + vw->height = qcam->height; + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; } return 0; } @@ -675,13 +670,13 @@ static ssize_t qcam_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *v = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)v; + struct qcam_device *qcam = (struct qcam_device *)v; int len; mutex_lock(&qcam->lock); parport_claim_or_block(qcam->pdev); /* Probably should have a semaphore against multiple users */ - len = qc_capture(qcam, buf,count); + len = qc_capture(qcam, buf, count); parport_release(qcam->pdev); mutex_unlock(&qcam->lock); return len; @@ -713,8 +708,7 @@ static const struct v4l2_file_operations qcam_fops = { .read = qcam_read, }; -static struct video_device qcam_template= -{ +static struct video_device qcam_template = { .name = "Colour QuickCam", .fops = &qcam_fops, .release = video_device_release_empty, @@ -727,17 +721,16 @@ static struct qcam_device *qcam_init(struct parport *port) struct qcam_device *q; q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); - if(q==NULL) + if (q == NULL) return NULL; q->pport = port; q->pdev = parport_register_device(port, "c-qcam", NULL, NULL, NULL, 0, NULL); - q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE)?1:0; + q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0; - if (q->pdev == NULL) - { + if (q->pdev == NULL) { printk(KERN_ERR "c-qcam: couldn't register for %s.\n", port->name); kfree(q); @@ -765,12 +758,11 @@ static int init_cqcam(struct parport *port) { struct qcam_device *qcam; - if (parport[0] != -1) - { + if (parport[0] != -1) { /* The user gave specific instructions */ int i, found = 0; - for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) - { + + for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) { if (parport[0] == port->number) found = 1; } @@ -782,15 +774,14 @@ static int init_cqcam(struct parport *port) return -ENOSPC; qcam = qcam_init(port); - if (qcam==NULL) + if (qcam == NULL) return -ENODEV; parport_claim_or_block(qcam->pdev); qc_reset(qcam); - if (probe && qc_detect(qcam)==0) - { + if (probe && qc_detect(qcam) == 0) { parport_release(qcam->pdev); parport_unregister_device(qcam->pdev); kfree(qcam); @@ -840,14 +831,14 @@ static struct parport_driver cqcam_driver = { .detach = cq_detach, }; -static int __init cqcam_init (void) +static int __init cqcam_init(void) { printk(BANNER "\n"); return parport_register_driver(&cqcam_driver); } -static void __exit cqcam_cleanup (void) +static void __exit cqcam_cleanup(void) { unsigned int i; @@ -862,9 +853,9 @@ MODULE_DESCRIPTION(BANNER); MODULE_LICENSE("GPL"); /* FIXME: parport=auto would never have worked, surely? --RR */ -MODULE_PARM_DESC(parport ,"parport=<auto|n[,n]...> for port detection method\n\ -probe=<0|1|2> for camera detection method\n\ -force_rgb=<0|1> for RGB data format (default BGR)"); +MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n" + "probe=<0|1|2> for camera detection method\n" + "force_rgb=<0|1> for RGB data format (default BGR)"); module_param_array(parport, int, NULL, 0); module_param(probe, int, 0); module_param(force_rgb, bool, 0); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 6f91415eb7b..5520789854d 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -324,7 +324,7 @@ static int cpia2_close(struct file *file) { if(fh->mmapped) cam->mmapped = 0; - v4l2_prio_close(&cam->prio,&fh->prio); + v4l2_prio_close(&cam->prio, fh->prio); file->private_data = NULL; kfree(fh); } @@ -1592,7 +1592,7 @@ static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_FMT: { struct cpia2_fh *fh = file->private_data; - retval = v4l2_prio_check(&cam->prio, &fh->prio); + retval = v4l2_prio_check(&cam->prio, fh->prio); if(retval) { mutex_unlock(&cam->busy_lock); return retval; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 4392c76af5d..0e5006b1427 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -579,6 +579,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, u8 afe_mux_cfg; u8 adc2_cfg; + u8 input_mode; u32 afe_cfg; int i; @@ -589,6 +590,30 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, vid_input <= CX18_AV_COMPOSITE8) { afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); ch[0] = CVBS; + input_mode = 0x0; + } else if (vid_input >= CX18_AV_COMPONENT_LUMA1) { + int luma = vid_input & 0xf000; + int r_chroma = vid_input & 0xf0000; + int b_chroma = vid_input & 0xf00000; + + if ((vid_input & ~0xfff000) || + luma < CX18_AV_COMPONENT_LUMA1 || + luma > CX18_AV_COMPONENT_LUMA8 || + r_chroma < CX18_AV_COMPONENT_R_CHROMA4 || + r_chroma > CX18_AV_COMPONENT_R_CHROMA6 || + b_chroma < CX18_AV_COMPONENT_B_CHROMA7 || + b_chroma > CX18_AV_COMPONENT_B_CHROMA8) { + CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", + vid_input); + return -EINVAL; + } + afe_mux_cfg = (luma - CX18_AV_COMPONENT_LUMA1) >> 12; + ch[0] = Y; + afe_mux_cfg |= (r_chroma - CX18_AV_COMPONENT_R_CHROMA4) >> 12; + ch[1] = Pr; + afe_mux_cfg |= (b_chroma - CX18_AV_COMPONENT_B_CHROMA7) >> 14; + ch[2] = Pb; + input_mode = 0x6; } else { int luma = vid_input & 0xf0; int chroma = vid_input & 0xf00; @@ -598,7 +623,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, luma > CX18_AV_SVIDEO_LUMA8 || chroma < CX18_AV_SVIDEO_CHROMA4 || chroma > CX18_AV_SVIDEO_CHROMA8) { - CX18_ERR_DEV(sd, "0x%04x is not a valid video input!\n", + CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", vid_input); return -EINVAL; } @@ -613,8 +638,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; ch[1] = C; } + input_mode = 0x2; } - /* TODO: LeadTek WinFast DVR3100 H & WinFast PVR2100 can do Y/Pb/Pr */ switch (aud_input) { case CX18_AV_AUDIO_SERIAL1: @@ -650,8 +675,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, /* Set up analog front end multiplexers */ cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7); - /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx18_av_and_or(cx, 0x401, ~0x6, ch[0] == CVBS ? 0 : 0x02); + /* Set INPUT_MODE to Composite, S-Video, or Component */ + cx18_av_and_or(cx, 0x401, ~0x6, input_mode); /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ adc2_cfg = cx18_av_read(cx, 0x102); @@ -998,9 +1023,9 @@ static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx18 *cx = v4l2_get_subdevdata(sd); - - return cx18_av_vbi_g_fmt(cx, fmt); + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return cx18_av_g_sliced_fmt(sd, &fmt->fmt.sliced); } static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) @@ -1073,12 +1098,6 @@ static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) cx18_av_write(cx, 0x41e, 0x8 | filter); break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx18_av_vbi_s_fmt(cx, fmt); - - case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx18_av_vbi_s_fmt(cx, fmt); - default: return -EINVAL; } @@ -1378,17 +1397,24 @@ static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { static const struct v4l2_subdev_video_ops cx18_av_video_ops = { .s_routing = cx18_av_s_video_routing, - .decode_vbi_line = cx18_av_decode_vbi_line, .s_stream = cx18_av_s_stream, .g_fmt = cx18_av_g_fmt, .s_fmt = cx18_av_s_fmt, }; +static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = { + .decode_vbi_line = cx18_av_decode_vbi_line, + .g_sliced_fmt = cx18_av_g_sliced_fmt, + .s_sliced_fmt = cx18_av_s_sliced_fmt, + .s_raw_fmt = cx18_av_s_raw_fmt, +}; + static const struct v4l2_subdev_ops cx18_av_ops = { .core = &cx18_av_general_ops, .tuner = &cx18_av_tuner_ops, .audio = &cx18_av_audio_ops, .video = &cx18_av_video_ops, + .vbi = &cx18_av_vbi_ops, }; int cx18_av_probe(struct cx18 *cx) diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index cafb7e99b9a..c106967bdcc 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -61,6 +61,25 @@ enum cx18_av_video_input { CX18_AV_SVIDEO2 = 0x620, CX18_AV_SVIDEO3 = 0x730, CX18_AV_SVIDEO4 = 0x840, + + /* Component Video inputs consist of one luma input (In1-In8) ORed + with a red chroma (In4-In6) and blue chroma input (In7-In8) */ + CX18_AV_COMPONENT_LUMA1 = 0x1000, + CX18_AV_COMPONENT_LUMA2 = 0x2000, + CX18_AV_COMPONENT_LUMA3 = 0x3000, + CX18_AV_COMPONENT_LUMA4 = 0x4000, + CX18_AV_COMPONENT_LUMA5 = 0x5000, + CX18_AV_COMPONENT_LUMA6 = 0x6000, + CX18_AV_COMPONENT_LUMA7 = 0x7000, + CX18_AV_COMPONENT_LUMA8 = 0x8000, + CX18_AV_COMPONENT_R_CHROMA4 = 0x40000, + CX18_AV_COMPONENT_R_CHROMA5 = 0x50000, + CX18_AV_COMPONENT_R_CHROMA6 = 0x60000, + CX18_AV_COMPONENT_B_CHROMA7 = 0x700000, + CX18_AV_COMPONENT_B_CHROMA8 = 0x800000, + + /* Component Video aliases for common combinations */ + CX18_AV_COMPONENT1 = 0x861000, }; enum cx18_av_audio_input { @@ -359,7 +378,8 @@ void cx18_av_audio_set_path(struct cx18 *cx); /* cx18_av-vbi.c */ int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); -int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt); -int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt); +int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); +int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); #endif diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index a51732bcca4..baa36fbcd4d 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -129,10 +129,10 @@ static int decode_vps(u8 *dst, u8 *p) return err & 0xf0; } -int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) +int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { + struct cx18 *cx = v4l2_get_subdevdata(sd); struct cx18_av_state *state = &cx->av_state; - struct v4l2_sliced_vbi_format *svbi; static const u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ @@ -143,9 +143,6 @@ int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) int is_pal = !(state->std & V4L2_STD_525_60); int i; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; memset(svbi, 0, sizeof(*svbi)); /* we're done if raw VBI is active */ if ((cx18_av_read(cx, 0x404) & 0x10) == 0) @@ -173,30 +170,27 @@ int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) return 0; } -int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt) +int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { + struct cx18 *cx = v4l2_get_subdevdata(sd); struct cx18_av_state *state = &cx->av_state; - struct v4l2_sliced_vbi_format *svbi; - int is_pal = !(state->std & V4L2_STD_525_60); - int i, x; - u8 lcr[24]; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); + /* Setup standard */ + cx18_av_std_setup(cx); - /* Setup standard */ - cx18_av_std_setup(cx); + /* VBI Offset */ + cx18_av_write(cx, 0x47f, state->slicer_line_delay); + cx18_av_write(cx, 0x404, 0x2e); + return 0; +} - /* VBI Offset */ - cx18_av_write(cx, 0x47f, state->slicer_line_delay); - cx18_av_write(cx, 0x404, 0x2e); - return 0; - } +int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + struct cx18_av_state *state = &cx->av_state; + int is_pal = !(state->std & V4L2_STD_525_60); + int i, x; + u8 lcr[24]; for (x = 0; x <= 23; x++) lcr[x] = 0x00; diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index f808fb6fc1c..74e122b5fc4 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -370,6 +370,7 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, @@ -422,6 +423,7 @@ static const struct cx18_card cx18_card_leadtek_dvr3100h = { { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, @@ -480,7 +482,7 @@ int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input) "S-Video 2", "Composite 1", "Composite 2", - "Composite 3" + "Component 1" }; memset(input, 0, sizeof(*input)); diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index af3d71607dc..796e517300a 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -43,7 +43,7 @@ #define CX18_CARD_INPUT_SVIDEO2 3 #define CX18_CARD_INPUT_COMPOSITE1 4 #define CX18_CARD_INPUT_COMPOSITE2 5 -#define CX18_CARD_INPUT_COMPOSITE3 6 +#define CX18_CARD_INPUT_COMPONENT1 6 /* audio inputs */ #define CX18_CARD_INPUT_AUD_TUNER 1 @@ -62,7 +62,7 @@ struct cx18_card_video_input { u8 video_type; /* video input type */ u8 audio_index; /* index in cx18_card_audio_input array */ - u16 video_input; /* hardware video input */ + u32 video_input; /* hardware video input */ }; struct cx18_card_audio_input { diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 7fa589240ff..4b4b46544d5 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -263,7 +263,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) int ret; struct v4l2_control ctrl; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 863ce775823..e12a15020cd 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -700,7 +700,7 @@ int cx18_v4l2_close(struct file *filp) CX18_DEBUG_IOCTL("close() of %s\n", s->name); - v4l2_prio_close(&cx->prio, &id->prio); + v4l2_prio_close(&cx->prio, id->prio); /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index eecf29af916..cfa1f289b0f 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -109,7 +109,7 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = IR_TYPE_RC5; init_data->name = cx->card_name; diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index b81dd0ea8eb..2530fc54daa 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -208,7 +208,7 @@ static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in * fmt->fmt.sliced under valid calling conditions */ - if (v4l2_subdev_call(cx->sd_av, video, g_fmt, fmt)) + if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced)) return -EINVAL; /* Ensure V4L2 spec compliant output */ @@ -277,7 +277,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, int ret; int w, h; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -306,7 +306,7 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -322,7 +322,7 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid * calling conditions */ - ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); + ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi); if (ret) return ret; @@ -341,7 +341,7 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, int ret; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -359,7 +359,7 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, * Note, cx18_av_vbi() wipes some "impossible" service lines in the * passed in fmt->fmt.sliced under valid calling conditions */ - ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); + ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced); if (ret) return ret; /* Store our current v4l2 sliced VBI settings */ @@ -549,7 +549,7 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -601,7 +601,7 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -647,7 +647,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -675,7 +675,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -715,7 +715,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 054450f65a6..f5c91261b2d 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -374,7 +374,10 @@ static void cx18_vbi_setup(struct cx18_stream *s) } /* setup VBI registers */ - v4l2_subdev_call(cx->sd_av, video, s_fmt, &cx->vbi.in); + if (raw) + v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi); + else + v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced); /* * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index 574c1c6974f..582227522cf 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -174,7 +174,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, p[3] != sliced_vbi_eav_rp[1])) continue; vbi.p = p + 4; - v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi); + v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi); if (vbi.type) { cx->vbi.sliced_data[line].id = vbi.type; cx->vbi.sliced_data[line].field = vbi.is_second_field; diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c index 7793d60966d..7cae95a2245 100644 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ b/drivers/media/video/cx231xx/cx231xx-audio.c @@ -495,7 +495,7 @@ static int cx231xx_audio_init(struct cx231xx *dev) pcm->info_flags = 0; pcm->private_data = dev; strcpy(pcm->name, "Conexant cx231xx Capture"); - strcpy(card->driver, "Conexant cx231xx Audio"); + strcpy(card->driver, "Cx231xx-Audio"); strcpy(card->shortname, "Cx231xx Audio"); strcpy(card->longname, "Conexant cx231xx Audio"); diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index b24eee115e7..f5e1a2315fc 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -96,10 +96,9 @@ int cx231xx_register_extension(struct cx231xx_ops *ops) mutex_lock(&cx231xx_devlist_mutex); mutex_lock(&cx231xx_extension_devlist_lock); list_add_tail(&ops->next, &cx231xx_extension_devlist); - list_for_each_entry(dev, &cx231xx_devlist, devlist) { - if (dev) - ops->init(dev); - } + list_for_each_entry(dev, &cx231xx_devlist, devlist) + ops->init(dev); + printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name); mutex_unlock(&cx231xx_extension_devlist_lock); mutex_unlock(&cx231xx_devlist_mutex); @@ -112,10 +111,8 @@ void cx231xx_unregister_extension(struct cx231xx_ops *ops) struct cx231xx *dev = NULL; mutex_lock(&cx231xx_devlist_mutex); - list_for_each_entry(dev, &cx231xx_devlist, devlist) { - if (dev) - ops->fini(dev); - } + list_for_each_entry(dev, &cx231xx_devlist, devlist) + ops->fini(dev); mutex_lock(&cx231xx_extension_devlist_lock); printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name); diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index b473cd8367f..fd099153b74 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -35,6 +35,8 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); +#define MODULE_NAME "cx231xx" + #define i2cdprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ @@ -59,7 +61,6 @@ struct cx231xx_ir_poll_result { struct cx231xx_IR { struct cx231xx *dev; struct input_dev *input; - struct ir_input_state ir; char name[32]; char phys[32]; @@ -67,9 +68,7 @@ struct cx231xx_IR { int polling; struct work_struct work; struct timer_list timer; - unsigned int last_toggle:1; unsigned int last_readcount; - unsigned int repeat_interval; int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *); }; @@ -81,7 +80,6 @@ struct cx231xx_IR { static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) { int result; - int do_sendkey = 0; struct cx231xx_ir_poll_result poll_result; /* read the registers containing the IR status */ @@ -95,44 +93,23 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) poll_result.toggle_bit, poll_result.read_count, ir->last_readcount, poll_result.rc_data[0]); - if (ir->dev->chip_id == CHIP_ID_EM2874) { + if (poll_result.read_count > 0 && + poll_result.read_count != ir->last_readcount) + 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; - } - - 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; - } - } + else + ir->last_readcount = poll_result.read_count; - if (do_sendkey) { - dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]); - ir_input_nokey(ir->input, &ir->ir); } - - ir->last_readcount = poll_result.read_count; - return; } static void ir_timer(unsigned long data) @@ -198,10 +175,6 @@ int cx231xx_ir_init(struct cx231xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - 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; @@ -217,7 +190,8 @@ int cx231xx_ir_init(struct cx231xx *dev) cx231xx_ir_start(ir); /* all done */ - err = ir_input_register(ir->input, dev->board.ir_codes, NULL); + err = __ir_input_register(ir->input, dev->board.ir_codes, + NULL, MODULE_NAME); if (err) goto err_out_stop; diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 16a73eab672..2782709b263 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -1669,7 +1669,7 @@ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, f->fmt.sliced.service_set = 0; - call_all(dev, video, g_fmt, f); + call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); if (f->fmt.sliced.service_set == 0) rc = -EINVAL; @@ -1690,7 +1690,7 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - call_all(dev, video, g_fmt, f); + call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); mutex_unlock(&dev->lock); if (f->fmt.sliced.service_set == 0) @@ -1902,9 +1902,12 @@ static int radio_queryctrl(struct file *file, void *priv, if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) return -EINVAL; if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX231XX_CTLS; i++) + for (i = 0; i < CX231XX_CTLS; i++) { if (cx231xx_ctls[i].v.id == c->id) break; + } + if (i == CX231XX_CTLS) + return -EINVAL; *c = cx231xx_ctls[i].v; } else *c = no_ctl; diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index 17d4d1a800c..38d417191a6 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -32,7 +32,7 @@ #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> -#include <media/ir-kbd-i2c.h> +#include <media/ir-core.h> #if defined(CONFIG_VIDEO_CX231XX_DVB) || \ defined(CONFIG_VIDEO_CX231XX_DVB_MODULE) #include <media/videobuf-dvb.h> diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 4c8e95853fa..022b480918c 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -1000,20 +1000,6 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, h, w); if (err) return err; } - - if (new->width != 720 || new->height != (new->is_50hz ? 576 : 480)) { - /* Adjust temporal filter if necessary. The problem with the - temporal filter is that it works well with full resolution - capturing, but not when the capture window is scaled (the - filter introduces a ghosting effect). So if the capture - window is scaled, then force the filter to 0. - - For full resolution the filter really improves the video - quality, especially if the original video quality is - suboptimal. */ - temporal = 0; - } - if (force || NEQ(stream_type)) { err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[new->stream_type]); diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c index d4a9d2c5947..c95e7bc1474 100644 --- a/drivers/media/video/cx23885/cimax2.c +++ b/drivers/media/video/cx23885/cimax2.c @@ -60,12 +60,18 @@ static unsigned int ci_dbg; module_param(ci_dbg, int, 0644); MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); +static unsigned int ci_irq_enable; +module_param(ci_irq_enable, int, 0644); +MODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM"); + #define ci_dbg_print(args...) \ do { \ if (ci_dbg) \ printk(KERN_DEBUG args); \ } while (0) +#define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0) + /* stores all private variables for communication with CI */ struct netup_ci_state { struct dvb_ca_en50221 ca; @@ -392,7 +398,7 @@ int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open if (0 != slot) return -EINVAL; - netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | NETUP_IRQ_IRQAM) + netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags()) : NETUP_IRQ_DETAM); return state->status; @@ -429,7 +435,7 @@ int netup_ci_init(struct cx23885_tsport *port) 0x01, /* power on (use it like store place) */ 0x00, /* RFU */ 0x00, /* int status read only */ - NETUP_IRQ_IRQAM | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ + ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ 0x05, /* EXTINT=active-high, INT=push-pull */ 0x00, /* USCG1 */ 0x04, /* ack active low */ @@ -470,7 +476,7 @@ int netup_ci_init(struct cx23885_tsport *port) state->ca.poll_slot_status = netup_poll_ci_slot_status; state->ca.data = state; state->priv = port; - state->current_irq_mode = NETUP_IRQ_IRQAM | NETUP_IRQ_DETAM; + state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM; ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 0, &cimax_init[0], 34); diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index a8ddc227cf8..abd64e89f60 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -1356,7 +1356,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; struct cx23885_tsport *tsport = &dev->ts1; - strcpy(cap->driver, dev->name); + strlcpy(cap->driver, dev->name, sizeof(cap->driver)); strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 939079d7bbb..9e1460828b2 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -1006,6 +1006,22 @@ static int dvb_register(struct cx23885_tsport *port) netup_ci_init(port); break; } + case CX23885_BOARD_TEVII_S470: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + + if (port->nr != 1) + break; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); + printk(KERN_INFO "TeVii S470 MAC= " + "%02X:%02X:%02X:%02X:%02X:%02X\n", + eeprom[0xa0], eeprom[0xa1], eeprom[0xa2], + eeprom[0xa3], eeprom[0xa4], eeprom[0xa5]); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); + break; + } } return ret; diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index 8e9d990dbe9..8d306d8bb61 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -51,6 +51,8 @@ #define RC5_EXTENDED_COMMAND_OFFSET 64 +#define MODULE_NAME "cx23885" + static inline unsigned int rc5_command(u32 rc5_baseband) { return RC5_INSTR(rc5_baseband) + @@ -338,7 +340,7 @@ int cx23885_input_init(struct cx23885_dev *dev) { struct card_ir *ir; struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; int ir_type, ir_addr, ir_start; int ret; @@ -353,7 +355,7 @@ int cx23885_input_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: /* Parameters for the grey Hauppauge remote for the HVR-1850 */ - ir_codes = &ir_codes_hauppauge_new_table; + 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 */ @@ -398,7 +400,7 @@ int cx23885_input_init(struct cx23885_dev *dev) dev->ir_input = ir; cx23885_input_ir_start(dev); - ret = ir_input_register(ir->dev, ir_codes, NULL); + ret = ir_input_register(ir->dev, ir_codes, NULL, MODULE_NAME); if (ret) goto err_out_stop; diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 2d3ac8b83dc..543b854f6a6 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -514,8 +514,8 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index f2461cd3de5..8b6fb354437 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1018,7 +1018,7 @@ static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi_g_fmt(sd, fmt); + return cx25840_g_sliced_fmt(sd, &fmt->fmt.sliced); default: return -EINVAL; } @@ -1079,12 +1079,6 @@ static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) cx25840_write(client, 0x41e, 0x8 | filter); break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi_s_fmt(sd, fmt); - - case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx25840_vbi_s_fmt(sd, fmt); - default: return -EINVAL; } @@ -1635,15 +1629,22 @@ static const struct v4l2_subdev_video_ops cx25840_video_ops = { .s_routing = cx25840_s_video_routing, .g_fmt = cx25840_g_fmt, .s_fmt = cx25840_s_fmt, - .decode_vbi_line = cx25840_decode_vbi_line, .s_stream = cx25840_s_stream, }; +static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { + .decode_vbi_line = cx25840_decode_vbi_line, + .s_raw_fmt = cx25840_s_raw_fmt, + .s_sliced_fmt = cx25840_s_sliced_fmt, + .g_sliced_fmt = cx25840_g_sliced_fmt, +}; + static const struct v4l2_subdev_ops cx25840_ops = { .core = &cx25840_core_ops, .tuner = &cx25840_tuner_ops, .audio = &cx25840_audio_ops, .video = &cx25840_video_ops, + .vbi = &cx25840_vbi_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 55345444417..04393b97156 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -99,8 +99,9 @@ int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ -int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); -int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +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); #endif diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index 35f6592f6c4..64a4004f8a9 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -82,11 +82,10 @@ static int decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct cx25840_state *state = to_state(sd); - struct v4l2_sliced_vbi_format *svbi; static const u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ @@ -97,9 +96,6 @@ int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) int is_pal = !(state->std & V4L2_STD_525_60); int i; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; memset(svbi, 0, sizeof(*svbi)); /* we're done if raw VBI is active */ if ((cx25840_read(client, 0x404) & 0x10) == 0) @@ -127,32 +123,30 @@ int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } -int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct cx25840_state *state = to_state(sd); - struct v4l2_sliced_vbi_format *svbi; int is_pal = !(state->std & V4L2_STD_525_60); int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); + /* Setup standard */ + cx25840_std_setup(client); - /* Setup standard */ - cx25840_std_setup(client); + /* VBI Offset */ + cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + return 0; +} - /* VBI Offset */ - cx25840_write(client, 0x47f, vbi_offset); - cx25840_write(client, 0x404, 0x2e); - return 0; - } +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; for (x = 0; x <= 23; x++) lcr[x] = 0x00; diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index b35411160f0..8b21457111b 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -847,7 +847,8 @@ static int set_tvaudio(struct cx88_core *core) { v4l2_std_id norm = core->tvnorm; - if (CX88_VMUX_TELEVISION != INPUT(core->input).type) + if (CX88_VMUX_TELEVISION != INPUT(core->input).type && + CX88_VMUX_CABLE != INPUT(core->input).type) return 0; if (V4L2_STD_PAL_BG & norm) { diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 94ab862f021..faa8e8163a4 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -1231,7 +1231,7 @@ static int dvb_register(struct cx8802_dev *dev) fe->ops.tuner_ops.set_config(fe, &ctl); } break; - case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_PINNACLE_HYBRID_PCTV: case CX88_BOARD_WINFAST_DTV1800H: fe0->dvb.frontend = dvb_attach(zl10353_attach, &cx88_pinnacle_hybrid_pctv, diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 6b6abf062c2..e185289e446 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -32,12 +32,18 @@ #include "cx88.h" #include <media/ir-common.h> +#define MODULE_NAME "cx88xx" + /* ---------------------------------------------------------------------- */ struct cx88_IR { struct cx88_core *core; struct input_dev *input; struct ir_input_state ir; + struct ir_dev_props props; + + int users; + char name[32]; char phys[32]; @@ -159,8 +165,16 @@ static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) return HRTIMER_RESTART; } -void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) +static int __cx88_ir_start(void *priv) { + struct cx88_core *core = priv; + struct cx88_IR *ir; + + if (!core || !core->ir) + return -EINVAL; + + ir = core->ir; + if (ir->polling) { hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ir->timer.function = cx88_ir_work; @@ -173,10 +187,18 @@ void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */ cx_write(MO_DDSCFG_IO, 0x5); /* enable */ } + return 0; } -void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) +static void __cx88_ir_stop(void *priv) { + struct cx88_core *core = priv; + struct cx88_IR *ir; + + if (!core || !core->ir) + return; + + ir = core->ir; if (ir->sampling) { cx_write(MO_DDSCFG_IO, 0x0); core->pci_irqmask &= ~PCI_INT_IR_SMPINT; @@ -186,15 +208,49 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) hrtimer_cancel(&ir->timer); } +int cx88_ir_start(struct cx88_core *core) +{ + if (core->ir->users) + return __cx88_ir_start(core); + + return 0; +} + +void cx88_ir_stop(struct cx88_core *core) +{ + if (core->ir->users) + __cx88_ir_stop(core); +} + +static int cx88_ir_open(void *priv) +{ + struct cx88_core *core = priv; + + core->ir->users++; + return __cx88_ir_start(core); +} + +static void cx88_ir_close(void *priv) +{ + struct cx88_core *core = priv; + + core->ir->users--; + if (!core->ir->users) + __cx88_ir_stop(core); +} + /* ---------------------------------------------------------------------- */ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) { struct cx88_IR *ir; struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; + u32 hardware_mask = 0; /* For devices with a hardware mask, when + * used with a full-code IR table + */ ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); @@ -208,15 +264,15 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_DNTV_LIVE_DVB_T: case CX88_BOARD_KWORLD_DVB_T: case CX88_BOARD_KWORLD_DVB_T_CX22702: - ir_codes = &ir_codes_dntv_live_dvb_t_table; + ir_codes = RC_MAP_DNTV_LIVE_DVB_T; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x60; ir->polling = 50; /* ms */ break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: - ir_codes = &ir_codes_cinergy_1400_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_CINERGY_1400; + ir_type = IR_TYPE_NEC; ir->sampling = 0xeb04; /* address */ break; case CX88_BOARD_HAUPPAUGE: @@ -230,14 +286,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: case CX88_BOARD_HAUPPAUGE_IRONLY: - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; ir_type = IR_TYPE_RC5; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_WINFAST_DTV1800H: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; @@ -246,14 +302,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_WINFAST2000XP_EXPERT: case CX88_BOARD_WINFAST_DTV1000: case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; ir->polling = 1; /* ms */ break; case CX88_BOARD_IODATA_GVBCTV7E: - ir_codes = &ir_codes_iodata_bctv7e_table; + ir_codes = RC_MAP_IODATA_BCTV7E; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0xfd; ir->mask_keydown = 0x02; @@ -261,36 +317,43 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_PROLINK_PLAYTVPVR: case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: - ir_codes = &ir_codes_pixelview_table; + /* + * It seems that this hardware is paired with NEC extended + * address 0x866b. So, unfortunately, its usage with other + * IR's with different address won't work. Still, there are + * other IR's from the same manufacturer that works, like the + * 002-T mini RC, provided with newer PV hardware + */ + ir_codes = RC_MAP_PIXELVIEW_MK12; ir->gpio_addr = MO_GP1_IO; - ir->mask_keycode = 0x1f; ir->mask_keyup = 0x80; - ir->polling = 1; /* ms */ + ir->polling = 10; /* ms */ + hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */ break; case CX88_BOARD_PROLINK_PV_8000GT: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: - ir_codes = &ir_codes_pixelview_new_table; + ir_codes = RC_MAP_PIXELVIEW_NEW; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x3f; ir->mask_keyup = 0x80; ir->polling = 1; /* ms */ break; case CX88_BOARD_KWORLD_LTV883: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x60; ir->polling = 1; /* ms */ break; case CX88_BOARD_ADSTECH_DVB_T_PCI: - ir_codes = &ir_codes_adstech_dvb_t_pci_table; + ir_codes = RC_MAP_ADSTECH_DVB_T_PCI; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0xbf; ir->mask_keyup = 0x40; ir->polling = 50; /* ms */ break; case CX88_BOARD_MSI_TVANYWHERE_MASTER: - ir_codes = &ir_codes_msi_tvanywhere_table; + ir_codes = RC_MAP_MSI_TVANYWHERE; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x40; @@ -298,7 +361,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_AVERTV_303: case CX88_BOARD_AVERTV_STUDIO_303: - ir_codes = &ir_codes_avertv_303_table; + ir_codes = RC_MAP_AVERTV_303; ir->gpio_addr = MO_GP2_IO; ir->mask_keycode = 0xfb; ir->mask_keydown = 0x02; @@ -311,41 +374,41 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PROF_7300: case CX88_BOARD_PROF_7301: case CX88_BOARD_PROF_6200: - ir_codes = &ir_codes_tbs_nec_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_TBS_NEC; + ir_type = IR_TYPE_NEC; ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_TEVII_S460: case CX88_BOARD_TEVII_S420: - ir_codes = &ir_codes_tevii_nec_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_TEVII_NEC; + ir_type = IR_TYPE_NEC; ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: - ir_codes = &ir_codes_dntv_live_dvbt_pro_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO; + ir_type = IR_TYPE_NEC; ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_NORWOOD_MICRO: - ir_codes = &ir_codes_norwood_table; + ir_codes = RC_MAP_NORWOOD; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x0e; ir->mask_keyup = 0x80; ir->polling = 50; /* ms */ break; case CX88_BOARD_NPGTECH_REALTV_TOP10FM: - ir_codes = &ir_codes_npgtech_table; + ir_codes = RC_MAP_NPGTECH; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0xfa; ir->polling = 50; /* ms */ break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: - ir_codes = &ir_codes_pinnacle_pctv_hd_table; + ir_codes = RC_MAP_PINNACLE_PCTV_HD; ir_type = IR_TYPE_RC5; ir->sampling = 1; break; case CX88_BOARD_POWERCOLOR_REAL_ANGEL: - ir_codes = &ir_codes_powercolor_real_angel_table; + ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL; ir->gpio_addr = MO_GP2_IO; ir->mask_keycode = 0x7e; ir->polling = 100; /* ms */ @@ -357,6 +420,21 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) goto err_out_free; } + /* + * The usage of mask_keycode were very convenient, due to several + * reasons. Among others, the scancode tables were using the scancode + * as the index elements. So, the less bits it was used, the smaller + * the table were stored. After the input changes, the better is to use + * the full scancodes, since it allows replacing the IR remote by + * another one. Unfortunately, there are still some hardware, like + * Pixelview Ultra Pro, where only part of the scancode is sent via + * GPIO. So, there's no way to get the full scancode. Due to that, + * hardware_mask were introduced here: it represents those hardware + * that has such limits. + */ + if (hardware_mask && !ir->mask_keycode) + ir->mask_keycode = hardware_mask; + /* init input device */ snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); @@ -381,19 +459,20 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->core = core; core->ir = ir; - cx88_ir_start(core, ir); + ir->props.priv = core; + ir->props.open = cx88_ir_open; + ir->props.close = cx88_ir_close; + ir->props.scanmask = hardware_mask; /* all done */ - err = ir_input_register(ir->input, ir_codes, NULL); + err = ir_input_register(ir->input, ir_codes, &ir->props, MODULE_NAME); if (err) - goto err_out_stop; + goto err_out_free; return 0; - err_out_stop: - cx88_ir_stop(core, ir); - core->ir = NULL; err_out_free: + core->ir = NULL; kfree(ir); return err; } @@ -406,7 +485,7 @@ int cx88_ir_fini(struct cx88_core *core) if (NULL == ir) return 0; - cx88_ir_stop(core, ir); + cx88_ir_stop(core); ir_input_unregister(ir->input); kfree(ir); diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 6aba7af9160..499f8d512ad 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -599,13 +599,22 @@ struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board static int cx8802_request_acquire(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; + unsigned int i; /* Fail a request for hardware if the device is busy. */ if (core->active_type_id != CX88_BOARD_NONE && core->active_type_id != drv->type_id) return -EBUSY; - core->input = CX88_VMUX_DVB; + core->input = 0; + for (i = 0; + i < (sizeof(core->board.input) / sizeof(struct cx88_input)); + i++) { + if (core->board.input[i].type == CX88_VMUX_DVB) { + core->input = i; + break; + } + } if (drv->advise_acquire) { diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 48c450f4a85..0fab65c3ab3 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -426,12 +426,13 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input) if (core->board.audio_chip && core->board.audio_chip == V4L2_IDENT_WM8775) { call_all(core, audio, s_routing, - INPUT(input).audioroute, 0, 0); + INPUT(input).audioroute, 0, 0); } /* cx2388's C-ADC is connected to the tuner only. When used with S-Video, that ADC is busy dealing with chroma, so an external must be used for baseband audio */ - if (INPUT(input).type != CX88_VMUX_TELEVISION ) { + if (INPUT(input).type != CX88_VMUX_TELEVISION && + INPUT(input).type != CX88_VMUX_CABLE) { /* "I2S ADC mode" */ core->tvaudio = WW_I2SADC; cx88_set_tvaudio(core); @@ -561,8 +562,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } @@ -1537,9 +1538,12 @@ static int radio_queryctrl (struct file *file, void *priv, c->id >= V4L2_CID_LASTP1) return -EINVAL; if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX8800_CTLS; i++) + for (i = 0; i < CX8800_CTLS; i++) { if (cx8800_ctls[i].v.id == c->id) break; + } + if (i == CX8800_CTLS) + return -EINVAL; *c = cx8800_ctls[i].v; } else *c = no_ctl; @@ -1977,7 +1981,7 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) } if (core->ir) - cx88_ir_stop(core, core->ir); + cx88_ir_stop(core); cx88_shutdown(core); /* FIXME */ pci_disable_device(pci_dev); @@ -2015,7 +2019,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) spin_unlock(&dev->slock); if (core->ir) - cx88_ir_stop(core, core->ir); + cx88_ir_stop(core); /* FIXME -- shutdown device */ cx88_shutdown(core); @@ -2056,7 +2060,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) /* FIXME: re-initialize hardware */ cx88_reset(core); if (core->ir) - cx88_ir_start(core, core->ir); + cx88_ir_start(core); cx_set(MO_PCI_INTMSK, core->pci_irqmask); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 48b6c04fb49..bdb03d33653 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -41,7 +41,7 @@ #include <linux/version.h> #include <linux/mutex.h> -#define CX88_VERSION_CODE KERNEL_VERSION(0,0,7) +#define CX88_VERSION_CODE KERNEL_VERSION(0, 0, 8) #define UNSET (-1U) @@ -290,7 +290,7 @@ struct cx88_subid { #define RESOURCE_VIDEO 2 #define RESOURCE_VBI 4 -#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* buffer for one video frame */ struct cx88_buffer { @@ -683,8 +683,8 @@ s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core); int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); int cx88_ir_fini(struct cx88_core *core); void cx88_ir_irq(struct cx88_core *core); -void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir); -void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir); +int cx88_ir_start(struct cx88_core *core); +void cx88_ir_stop(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-mpeg.c */ diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c index b4cc96dc99e..490aafb34e2 100644 --- a/drivers/media/video/davinci/dm644x_ccdc.c +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -101,6 +101,9 @@ static u32 ccdc_raw_bayer_pix_formats[] = static u32 ccdc_raw_yuv_pix_formats[] = {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; +/* CCDC Save/Restore context */ +static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)]; + /* register access routines */ static inline u32 regr(u32 offset) { @@ -400,7 +403,11 @@ void ccdc_config_ycbcr(void) * configure the FID, VD, HD pin polarity, * fld,hd pol positive, vd negative, 8-bit data */ - syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE | CCDC_SYN_MODE_8BITS; + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + syn_mode |= CCDC_SYN_MODE_10BITS; + else + syn_mode |= CCDC_SYN_MODE_8BITS; } else { /* y/c external sync mode */ syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << @@ -419,8 +426,13 @@ void ccdc_config_ycbcr(void) * configure the order of y cb cr in SDRAM, and disable latch * internal register on vsync */ - regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | - CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, + CCDC_CCDCFG); + else + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); /* * configure the horizontal line offset. This should be a @@ -435,7 +447,6 @@ void ccdc_config_ycbcr(void) ccdc_sbl_reset(); dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); - ccdc_readregs(); } static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) @@ -827,6 +838,7 @@ static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) case VPFE_BT656: case VPFE_YCBCR_SYNC_16: case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: ccdc_cfg.ycbcr.vd_pol = params->vdpol; ccdc_cfg.ycbcr.hd_pol = params->hdpol; break; @@ -837,6 +849,87 @@ static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) return 0; } +static void ccdc_save_context(void) +{ + ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR); + ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE); + ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID); + ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES); + ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO); + ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START); + ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES); + ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING); + ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF); + ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST); + ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR); + ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP); + ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB); + ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN); + ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP); + ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC); + ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR); + ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT); + ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW); + ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF); + ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG); + ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG); + ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ); + ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT); + ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0); + ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1); + ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2); + ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3); + ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4); + ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5); + ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6); + ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7); + ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0); + ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1); + ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0); + ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1); + ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT); +} + +static void ccdc_restore_context(void) +{ + regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE); + regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID); + regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES); + regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO); + regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START); + regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES); + regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING); + regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF); + regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST); + regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR); + regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP); + regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB); + regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN); + regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP); + regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC); + regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR); + regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT); + regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW); + regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF); + regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG); + regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG); + regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ); + regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT); + regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0); + regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1); + regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2); + regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3); + regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4); + regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5); + regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6); + regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7); + regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0); + regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1); + regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0); + regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1); + regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT); + regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR); +} static struct ccdc_hw_device ccdc_hw_dev = { .name = "DM6446 CCDC", .owner = THIS_MODULE, @@ -945,10 +1038,40 @@ static int dm644x_ccdc_remove(struct platform_device *pdev) return 0; } +static int dm644x_ccdc_suspend(struct device *dev) +{ + /* Save CCDC context */ + ccdc_save_context(); + /* Disable CCDC */ + ccdc_enable(0); + /* Disable both master and slave clock */ + clk_disable(ccdc_cfg.mclk); + clk_disable(ccdc_cfg.sclk); + + return 0; +} + +static int dm644x_ccdc_resume(struct device *dev) +{ + /* Enable both master and slave clock */ + clk_enable(ccdc_cfg.mclk); + clk_enable(ccdc_cfg.sclk); + /* Restore CCDC context */ + ccdc_restore_context(); + + return 0; +} + +static const struct dev_pm_ops dm644x_ccdc_pm_ops = { + .suspend = dm644x_ccdc_suspend, + .resume = dm644x_ccdc_resume, +}; + static struct platform_driver dm644x_ccdc_driver = { .driver = { .name = "dm644x_ccdc", .owner = THIS_MODULE, + .pm = &dm644x_ccdc_pm_ops, }, .remove = __devexit_p(dm644x_ccdc_remove), .probe = dm644x_ccdc_probe, diff --git a/drivers/media/video/davinci/dm644x_ccdc_regs.h b/drivers/media/video/davinci/dm644x_ccdc_regs.h index 6e5d0532446..90370e414e2 100644 --- a/drivers/media/video/davinci/dm644x_ccdc_regs.h +++ b/drivers/media/video/davinci/dm644x_ccdc_regs.h @@ -59,7 +59,7 @@ #define CCDC_PRGODD_0 0x8c #define CCDC_PRGODD_1 0x90 #define CCDC_VP_OUT 0x94 - +#define CCDC_REG_END 0x98 /*************************************************************** * Define for various register bit mask and shifts for CCDC @@ -135,11 +135,19 @@ #define CCDC_SYN_MODE_INPMOD_SHIFT 12 #define CCDC_SYN_MODE_INPMOD_MASK 3 #define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_MODE_10BITS (6 << 8) +#define CCDC_SYN_MODE_11BITS (5 << 8) +#define CCDC_SYN_MODE_12BITS (4 << 8) +#define CCDC_SYN_MODE_13BITS (3 << 8) +#define CCDC_SYN_MODE_14BITS (2 << 8) +#define CCDC_SYN_MODE_15BITS (1 << 8) +#define CCDC_SYN_MODE_16BITS (0 << 8) #define CCDC_SYN_FLDMODE_MASK 1 #define CCDC_SYN_FLDMODE_SHIFT 7 #define CCDC_REC656IF_BT656_EN 3 #define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) #define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_CCDCFG_BW656_10BIT (1 << 5) #define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 #define CCDC_NO_CULLING 0xffff00ff #endif diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/video/davinci/isif_regs.h index f7b8893a295..aa69a463c12 100644 --- a/drivers/media/video/davinci/isif_regs.h +++ b/drivers/media/video/davinci/isif_regs.h @@ -158,7 +158,7 @@ /* gain - offset masks */ #define GAIN_INTEGER_SHIFT 9 -#define OFFSET_MASK 0xFFF +#define OFFSET_MASK 0xFFF #define GAIN_SDRAM_EN_SHIFT 12 #define GAIN_IPIPE_EN_SHIFT 13 #define GAIN_H3A_EN_SHIFT 14 diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 398dbe71cb8..1c258824728 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -475,6 +475,11 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); if (!ret) vpfe_dev->initialized = 1; + + /* Clear all VPFE/CCDC interrupts */ + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(-1); + unlock: mutex_unlock(&ccdc_lock); return ret; @@ -534,6 +539,16 @@ static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) list_del(&vpfe_dev->next_frm->queue); vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; ccdc_dev->hw_ops.setfbaddr(addr); } @@ -554,7 +569,6 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) { struct vpfe_device *vpfe_dev = dev_id; enum v4l2_field field; - unsigned long addr; int fid; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); @@ -562,7 +576,7 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) /* if streaming not started, don't do anything */ if (!vpfe_dev->started) - return IRQ_HANDLED; + goto clear_intr; /* only for 6446 this will be applicable */ if (NULL != ccdc_dev->hw_ops.reset) @@ -574,7 +588,7 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) "frame format is progressive...\n"); if (vpfe_dev->cur_frm != vpfe_dev->next_frm) vpfe_process_buffer_complete(vpfe_dev); - return IRQ_HANDLED; + goto clear_intr; } /* interlaced or TB capture check which field we are in hardware */ @@ -599,12 +613,9 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) * the CCDC memory address */ if (field == V4L2_FIELD_SEQ_TB) { - addr = - videobuf_to_dma_contig(vpfe_dev->cur_frm); - addr += vpfe_dev->field_off; - ccdc_dev->hw_ops.setfbaddr(addr); + vpfe_schedule_bottom_field(vpfe_dev); } - return IRQ_HANDLED; + goto clear_intr; } /* * if one field is just being captured configure @@ -624,6 +635,10 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) */ vpfe_dev->field_id = fid; } +clear_intr: + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; } @@ -635,8 +650,11 @@ static irqreturn_t vdint1_isr(int irq, void *dev_id) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); /* if streaming not started, don't do anything */ - if (!vpfe_dev->started) + if (!vpfe_dev->started) { + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); return IRQ_HANDLED; + } spin_lock(&vpfe_dev->dma_queue_lock); if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && @@ -644,6 +662,10 @@ static irqreturn_t vdint1_isr(int irq, void *dev_id) vpfe_dev->cur_frm == vpfe_dev->next_frm) vpfe_schedule_next_buffer(vpfe_dev); spin_unlock(&vpfe_dev->dma_queue_lock); + + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; } @@ -714,7 +736,7 @@ static int vpfe_release(struct file *file) /* Decrement device usrs counter */ vpfe_dev->usrs--; /* Close the priority */ - v4l2_prio_close(&vpfe_dev->prio, &fh->prio); + v4l2_prio_close(&vpfe_dev->prio, fh->prio); /* If this is the last file handle */ if (!vpfe_dev->usrs) { vpfe_dev->initialized = 0; @@ -1218,7 +1240,10 @@ static int vpfe_videobuf_setup(struct videobuf_queue *vq, struct vpfe_device *vpfe_dev = fh->vpfe_dev; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); - *size = config_params.device_bufsize; + *size = vpfe_dev->fmt.fmt.pix.sizeimage; + if (vpfe_dev->memory == V4L2_MEMORY_MMAP && + vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) + *size = config_params.device_bufsize; if (*count < config_params.min_numbuffers) *count = config_params.min_numbuffers; @@ -1233,6 +1258,8 @@ static int vpfe_videobuf_prepare(struct videobuf_queue *vq, { struct vpfe_fh *fh = vq->priv_data; struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long addr; + int ret; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); @@ -1242,8 +1269,18 @@ static int vpfe_videobuf_prepare(struct videobuf_queue *vq, vb->height = vpfe_dev->fmt.fmt.pix.height; vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; vb->field = field; + + ret = videobuf_iolock(vq, vb, NULL);; + if (ret < 0) + return ret; + + addr = videobuf_to_dma_contig(vb); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + vb->state = VIDEOBUF_PREPARED; } - vb->state = VIDEOBUF_PREPARED; return 0; } @@ -1311,13 +1348,6 @@ static int vpfe_reqbufs(struct file *file, void *priv, return -EINVAL; } - if (V4L2_MEMORY_USERPTR == req_buf->memory) { - /* we don't support user ptr IO */ - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs:" - " USERPTR IO not supported\n"); - return -EINVAL; - } - ret = mutex_lock_interruptible(&vpfe_dev->lock); if (ret) return ret; @@ -1831,7 +1861,6 @@ static __init int vpfe_probe(struct platform_device *pdev) goto probe_free_dev_mem; } - mutex_lock(&ccdc_lock); /* Allocate memory for ccdc configuration */ ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); if (NULL == ccdc_cfg) { @@ -1840,6 +1869,8 @@ static __init int vpfe_probe(struct platform_device *pdev) goto probe_free_lock; } + mutex_lock(&ccdc_lock); + strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); /* Get VINT0 irq resource */ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -2015,18 +2046,14 @@ static int __devexit vpfe_remove(struct platform_device *pdev) return 0; } -static int -vpfe_suspend(struct device *dev) +static int vpfe_suspend(struct device *dev) { - /* add suspend code here later */ - return -1; + return 0; } -static int -vpfe_resume(struct device *dev) +static int vpfe_resume(struct device *dev) { - /* add resume code here later */ - return -1; + return 0; } static const struct dev_pm_ops vpfe_dev_pm_ops = { diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 2e5a7fb2d0c..a7f48b53d3f 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -869,7 +869,7 @@ static int vpif_release(struct file *filep) mutex_unlock(&common->lock); /* Close the priority */ - v4l2_prio_close(&ch->prio, &fh->prio); + v4l2_prio_close(&ch->prio, fh->prio); if (fh->initialized) ch->initialized = 0; @@ -1444,7 +1444,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } } - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; @@ -1554,7 +1554,7 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) } } - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; @@ -1710,7 +1710,7 @@ static int vpif_s_fmt_vid_cap(struct file *file, void *priv, } } - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 13c3a1b9776..da07607cbc5 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -384,7 +384,7 @@ static int vpif_get_std_info(struct channel_obj *ch) int index; std_info->stdid = vid_ch->stdid; - if (!std_info) + if (!std_info->stdid) return -1; for (index = 0; index < ARRAY_SIZE(ch_params); index++) { @@ -671,7 +671,7 @@ static int vpif_release(struct file *filep) ch->initialized = 0; /* Close the priority */ - v4l2_prio_close(&ch->prio, &fh->prio); + v4l2_prio_close(&ch->prio, fh->prio); filep->private_data = NULL; fh->initialized = 0; kfree(fh); @@ -753,7 +753,7 @@ static int vpif_s_fmt_vid_out(struct file *file, void *priv, } /* Check for the priority */ - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; fh->initialized = 1; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index bd783387b37..e182abf476c 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -491,7 +491,7 @@ static int em28xx_audio_init(struct em28xx *dev) strcpy(pcm->name, "Empia 28xx Capture"); snd_card_set_dev(card, &dev->udev->dev); - strcpy(card->driver, "Empia Em28xx Audio"); + strcpy(card->driver, "Em28xx-Audio"); strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index b0fb0833771..3a4fd851451 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -602,7 +602,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Gadmei UTV330+", .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, - .ir_codes = &ir_codes_gadmei_rm008z_table, + .ir_codes = RC_MAP_GADMEI_RM008Z, .decoder = EM28XX_SAA711X, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { @@ -681,6 +681,20 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = { + .name = "EM2860/TVP5150 Reference Design", + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", .tuner_type = TUNER_TNF_5335MF, @@ -777,7 +791,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = RC_MAP_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -802,7 +816,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = RC_MAP_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -828,7 +842,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = RC_MAP_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -854,7 +868,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_rc5_hauppauge_new_table, + .ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -880,7 +894,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_pinnacle_pctv_hd_table, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -906,7 +920,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_ati_tv_wonder_hd_600_table, + .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -932,7 +946,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_digital, - .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1282,7 +1296,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA711X, .has_dvb = 1, .dvb_gpio = em2882_kworld_315u_digital, - .ir_codes = &ir_codes_kworld_315u_table, + .ir_codes = RC_MAP_KWORLD_315U, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, /* Analog mode - still not ready */ @@ -1404,10 +1418,14 @@ struct em28xx_board em28xx_boards[] = { }, [EM2882_BOARD_KWORLD_VS_DVBT] = { .name = "Kworld VS-DVB-T 323UR", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = kworld_330u_digital, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .ir_codes = RC_MAP_KWORLD_315U, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1430,7 +1448,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1523,7 +1541,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .decoder = EM28XX_TVP5150, .tuner_gpio = default_tuner_gpio, - .ir_codes = &ir_codes_kaiomy_table, + .ir_codes = RC_MAP_KAIOMY, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1623,7 +1641,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = evga_indtube_digital, - .ir_codes = &ir_codes_evga_indtube_table, + .ir_codes = RC_MAP_EVGA_INDTUBE, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1672,6 +1690,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2862), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2863), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2870), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2881), @@ -1792,6 +1812,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, + {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, }; @@ -2138,6 +2159,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) break; case EM2883_BOARD_KWORLD_HYBRID_330U: case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: ctl->demod = XC3028_FE_CHINA; ctl->fname = XC2028_DEFAULT_FIRMWARE; break; @@ -2313,21 +2335,21 @@ void em28xx_register_i2c_ir(struct em28xx *dev) switch (dev->model) { case EM2800_BOARD_TERRATEC_CINERGY_200: case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = &ir_codes_em_terratec_table; + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; dev->init_data.get_key = em28xx_get_key_terratec; dev->init_data.name = "i2c IR (EM28XX Terratec)"; break; case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; break; case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = &ir_codes_rc5_hauppauge_new_table; + dev->init_data.ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = &ir_codes_winfast_usbii_deluxe_table;; + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; break; diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index a41cc556677..d3813ed789d 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -782,11 +782,15 @@ int em28xx_resolution_set(struct em28xx *dev) em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - /* If we don't set the start position to 4 in VBI mode, we end up - with line 21 being YUYV encoded instead of being in 8-bit - greyscale */ + /* If we don't set the start position to 2 in VBI mode, we end up + with line 20/21 being YUYV encoded instead of being in 8-bit + greyscale. The core of the issue is that line 21 (and line 23 for + PAL WSS) are inside of active video region, and as a result they + get the pixelformatting associated with that area. So by cropping + it out, we end up with the same format as the rest of the VBI + region */ if (em28xx_vbi_supported(dev) == 1) - em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2); + em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2); else em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); @@ -1174,21 +1178,18 @@ void em28xx_add_into_devlist(struct em28xx *dev) */ static LIST_HEAD(em28xx_extension_devlist); -static DEFINE_MUTEX(em28xx_extension_devlist_lock); int em28xx_register_extension(struct em28xx_ops *ops) { struct em28xx *dev = NULL; mutex_lock(&em28xx_devlist_mutex); - mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); list_for_each_entry(dev, &em28xx_devlist, devlist) { if (dev) ops->init(dev); } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); - mutex_unlock(&em28xx_extension_devlist_lock); mutex_unlock(&em28xx_devlist_mutex); return 0; } @@ -1204,10 +1205,8 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) ops->fini(dev); } - mutex_lock(&em28xx_extension_devlist_lock); printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); list_del(&ops->next); - mutex_unlock(&em28xx_extension_devlist_lock); mutex_unlock(&em28xx_devlist_mutex); } EXPORT_SYMBOL(em28xx_unregister_extension); @@ -1216,26 +1215,26 @@ void em28xx_init_extension(struct em28xx *dev) { struct em28xx_ops *ops = NULL; - mutex_lock(&em28xx_extension_devlist_lock); + mutex_lock(&em28xx_devlist_mutex); if (!list_empty(&em28xx_extension_devlist)) { list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->init) ops->init(dev); } } - mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); } void em28xx_close_extension(struct em28xx *dev) { struct em28xx_ops *ops = NULL; - mutex_lock(&em28xx_extension_devlist_lock); + mutex_lock(&em28xx_devlist_mutex); if (!list_empty(&em28xx_extension_devlist)) { list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->fini) ops->fini(dev); } } - mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index bcd3c371009..cf1d8c3655f 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -467,6 +467,7 @@ static int dvb_init(struct em28xx *dev) } dev->dvb = dvb; + mutex_lock(&dev->lock); em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { @@ -506,6 +507,7 @@ static int dvb_init(struct em28xx *dev) case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: case EM2881_BOARD_PINNACLE_HYBRID_PRO: case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); @@ -589,15 +591,16 @@ static int dvb_init(struct em28xx *dev) if (result < 0) goto out_free; - em28xx_set_mode(dev, EM28XX_SUSPEND); em28xx_info("Successfully loaded em28xx-dvb\n"); - return 0; +ret: + em28xx_set_mode(dev, EM28XX_SUSPEND); + mutex_unlock(&dev->lock); + return result; out_free: - em28xx_set_mode(dev, EM28XX_SUSPEND); kfree(dvb); dev->dvb = NULL; - return result; + goto ret; } static int dvb_fini(struct em28xx *dev) diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 20a0001e888..5c3fd9411b1 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -39,6 +39,8 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); +#define MODULE_NAME "em28xx" + #define i2cdprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ @@ -360,14 +362,20 @@ static void em28xx_ir_work(struct work_struct *work) schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } -static void em28xx_ir_start(struct em28xx_IR *ir) +static int em28xx_ir_start(void *priv) { + struct em28xx_IR *ir = priv; + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); + + return 0; } -static void em28xx_ir_stop(struct em28xx_IR *ir) +static void em28xx_ir_stop(void *priv) { + struct em28xx_IR *ir = priv; + cancel_delayed_work_sync(&ir->work); } @@ -380,7 +388,6 @@ int em28xx_ir_change_protocol(void *priv, u64 ir_type) /* Adjust xclk based o IR table for RC5/NEC tables */ - dev->board.ir_codes->ir_type = IR_TYPE_OTHER; if (ir_type == IR_TYPE_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; @@ -388,11 +395,9 @@ int em28xx_ir_change_protocol(void *priv, u64 ir_type) dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; ir_config = EM2874_IR_NEC; ir->full_code = 1; - } else + } else if (ir_type != IR_TYPE_UNKNOWN) rc = -EINVAL; - dev->board.ir_codes->ir_type = ir_type; - em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -443,6 +448,13 @@ int em28xx_ir_init(struct em28xx *dev) ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; ir->props.priv = ir; ir->props.change_protocol = em28xx_ir_change_protocol; + ir->props.open = em28xx_ir_start; + ir->props.close = em28xx_ir_stop; + + /* By default, keep protocol field untouched */ + err = em28xx_ir_change_protocol(ir, IR_TYPE_UNKNOWN); + if (err) + goto err_out_free; /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -455,7 +467,6 @@ int em28xx_ir_init(struct em28xx *dev) strlcat(ir->phys, "/input0", sizeof(ir->phys)); /* Set IR protocol */ - em28xx_ir_change_protocol(ir, dev->board.ir_codes->ir_type); err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); if (err < 0) goto err_out_free; @@ -470,17 +481,15 @@ int em28xx_ir_init(struct em28xx *dev) input_dev->dev.parent = &dev->udev->dev; - em28xx_ir_start(ir); /* all done */ err = ir_input_register(ir->input, dev->board.ir_codes, - &ir->props); + &ir->props, MODULE_NAME); if (err) goto err_out_stop; return 0; err_out_stop: - em28xx_ir_stop(ir); dev->ir = NULL; err_out_free: kfree(ir); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 0fe20110bfd..20090e34173 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -203,12 +203,6 @@ static void em28xx_copy_video(struct em28xx *dev, if (dma_q->pos + len > buf->vb.size) len = buf->vb.size - dma_q->pos; - if (p[0] != 0x88 && p[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - len += 4; - } else - p += 4; - startread = p; remain = len; @@ -309,14 +303,6 @@ static void em28xx_copy_vbi(struct em28xx *dev, if (dma_q->pos + len > buf->vb.size) len = buf->vb.size - dma_q->pos; - if ((p[0] == 0x33 && p[1] == 0x95) || - (p[0] == 0x88 && p[1] == 0x88)) { - /* Header field, advance past it */ - p += 4; - } else { - len += 4; - } - startread = p; startwrite = outp + dma_q->pos; @@ -507,8 +493,15 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) dma_q->pos = 0; } - if (buf != NULL) + if (buf != NULL) { + if (p[0] != 0x88 && p[0] != 0x22) { + em28xx_isocdbg("frame is not complete\n"); + len += 4; + } else { + p += 4; + } em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } } return rc; } @@ -555,8 +548,7 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) continue; } - len = urb->iso_frame_desc[i].actual_length - 4; - + len = urb->iso_frame_desc[i].actual_length; if (urb->iso_frame_desc[i].actual_length <= 0) { /* em28xx_isocdbg("packet %d is empty",i); - spammy */ continue; @@ -577,6 +569,17 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) dev->vbi_read = 0; em28xx_isocdbg("VBI START HEADER!!!\n"); dev->cur_field = p[2]; + p += 4; + len -= 4; + } else if (p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + /* continuation */ + p += 4; + len -= 4; + } else if (p[0] == 0x22 && p[1] == 0x5a) { + /* start video */ + p += 4; + len -= 4; } vbi_size = dev->vbi_width * dev->vbi_height; @@ -631,9 +634,6 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if (dev->capture_type == 1) { dev->capture_type = 2; - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - if (dev->progressive || !(dev->cur_field & 1)) { if (buf != NULL) buffer_filled(dev, dma_q, buf); @@ -652,8 +652,25 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) dma_q->pos = 0; } - if (buf != NULL && dev->capture_type == 2) - em28xx_copy_video(dev, dma_q, buf, p, outp, len); + + if (buf != NULL && dev->capture_type == 2) { + 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) { + em28xx_isocdbg("Video frame %d, len=%i, %s\n", + p[2], len, (p[2] & 1) ? + "odd" : "even"); + p += 4; + len -= 4; + } + + if (len > 0) + em28xx_copy_video(dev, dma_q, buf, p, outp, + len); + } } return rc; } @@ -1483,6 +1500,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Tuner"); + t->type = V4L2_TUNER_ANALOG_TV; mutex_lock(&dev->lock); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); @@ -1814,7 +1832,7 @@ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, mutex_lock(&dev->lock); f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); if (f->fmt.sliced.service_set == 0) rc = -EINVAL; @@ -1836,7 +1854,7 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); mutex_unlock(&dev->lock); if (f->fmt.sliced.service_set == 0) diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index ba6fe5daff8..b252d1b1b2a 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -68,6 +68,7 @@ #define EM2820_BOARD_HERCULES_SMART_TV_USB2 26 #define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27 #define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28 +#define EM2860_BOARD_TVP5150_REFERENCE_DESIGN 29 #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 @@ -140,10 +141,10 @@ #define EM28XX_NUM_BUFS 5 /* number of packets for each buffer - windows requests only 40 packets .. so we better do the same + windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ -#define EM28XX_NUM_PACKETS 40 +#define EM28XX_NUM_PACKETS 64 #define EM28XX_INTERLACED_DEFAULT 1 @@ -411,7 +412,7 @@ struct em28xx_board { struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; - struct ir_scancode_table *ir_codes; + char *ir_codes; }; struct em28xx_eeprom { diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index e6c23d50986..a5cfc76b40b 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1713,7 +1713,7 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + 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; @@ -1723,7 +1723,9 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) 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; diff --git a/drivers/media/video/font.h b/drivers/media/video/font.h deleted file mode 100644 index 8b1fecc3759..00000000000 --- a/drivers/media/video/font.h +++ /dev/null @@ -1,407 +0,0 @@ -static unsigned char rom8x16_bits[] = { -/* Character 0 (0x30): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - |** *** | - |** **** | - |**** ** | - |*** ** | - |** ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0xce, -0xde, -0xf6, -0xe6, -0xc6, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 1 (0x31): - ht=16, width=8 - +--------+ - | | - | | - | ** | - | **** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ****** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x18, -0x78, -0x18, -0x18, -0x18, -0x18, -0x18, -0x18, -0x18, -0x7e, -0x00, -0x00, -0x00, -0x00, - -/* Character 2 (0x32): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - | ** | - | ** | - | ** | - | ** | - | ** | - |** ** | - |******* | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0x06, -0x0c, -0x18, -0x30, -0x60, -0xc6, -0xfe, -0x00, -0x00, -0x00, -0x00, - -/* Character 3 (0x33): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - | ** | - | ** | - | **** | - | ** | - | ** | - | ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0x06, -0x06, -0x3c, -0x06, -0x06, -0x06, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 4 (0x34): - ht=16, width=8 - +--------+ - | | - | | - | ** | - | *** | - | **** | - | ** ** | - |** ** | - |** ** | - |******* | - | ** | - | ** | - | **** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x0c, -0x1c, -0x3c, -0x6c, -0xcc, -0xcc, -0xfe, -0x0c, -0x0c, -0x1e, -0x00, -0x00, -0x00, -0x00, - -/* Character 5 (0x35): - ht=16, width=8 - +--------+ - | | - | | - |******* | - |** | - |** | - |** | - |****** | - | ** | - | ** | - | ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0xfe, -0xc0, -0xc0, -0xc0, -0xfc, -0x06, -0x06, -0x06, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 6 (0x36): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** | - |** | - |****** | - |** ** | - |** ** | - |** ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc0, -0xc0, -0xfc, -0xc6, -0xc6, -0xc6, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 7 (0x37): - ht=16, width=8 - +--------+ - | | - | | - |******* | - |** ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0xfe, -0xc6, -0x06, -0x0c, -0x18, -0x30, -0x30, -0x30, -0x30, -0x30, -0x00, -0x00, -0x00, -0x00, - -/* Character 8 (0x38): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - |** ** | - | ***** | - |** ** | - |** ** | - |** ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0xc6, -0x7c, -0xc6, -0xc6, -0xc6, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 9 (0x39): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - |** ** | - |** ** | - | ****** | - | ** | - | ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0xc6, -0xc6, -0x7e, -0x06, -0x06, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, -/* Character : (0x3a): - ht=16, width=8 - +--------+ - | | - | | - | | - | | - | | - | ** | - | ** | - | | - | | - | ** | - | ** | - | | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x00, -0x00, -0x00, -0x0c, -0x0c, -0x00, -0x00, -0x0c, -0x0c, -0x00, -0x00, -0x00, -0x00, -0x00, -}; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index e0060c1f054..5d920e584de 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -172,12 +172,6 @@ config USB_GSPCA_SN9C20X To compile this driver as a module, choose M here: the module will be called gspca_sn9c20x. -config USB_GSPCA_SN9C20X_EVDEV - bool "Enable evdev support" - depends on USB_GSPCA_SN9C20X && INPUT - ---help--- - Say Y here in order to enable evdev support for sn9c20x webcam button. - config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 82945ed5cbe..58b696f455b 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -374,7 +374,7 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -861,7 +861,7 @@ static int save_camera_state(struct gspca_dev *gspca_dev) return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); } -int command_setformat(struct gspca_dev *gspca_dev) +static int command_setformat(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -878,7 +878,7 @@ int command_setformat(struct gspca_dev *gspca_dev) sd->params.roi.rowStart, sd->params.roi.rowEnd); } -int command_setcolourparams(struct gspca_dev *gspca_dev) +static int command_setcolourparams(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; return do_command(gspca_dev, CPIA_COMMAND_SetColourParams, @@ -887,7 +887,7 @@ int command_setcolourparams(struct gspca_dev *gspca_dev) sd->params.colourParams.saturation, 0); } -int command_setapcor(struct gspca_dev *gspca_dev) +static int command_setapcor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; return do_command(gspca_dev, CPIA_COMMAND_SetApcor, @@ -897,7 +897,7 @@ int command_setapcor(struct gspca_dev *gspca_dev) sd->params.apcor.gain8); } -int command_setvloffset(struct gspca_dev *gspca_dev) +static int command_setvloffset(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset, @@ -907,7 +907,7 @@ int command_setvloffset(struct gspca_dev *gspca_dev) sd->params.vlOffset.gain8); } -int command_setexposure(struct gspca_dev *gspca_dev) +static int command_setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -943,7 +943,7 @@ int command_setexposure(struct gspca_dev *gspca_dev) return ret; } -int command_setcolourbalance(struct gspca_dev *gspca_dev) +static int command_setcolourbalance(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -973,7 +973,7 @@ int command_setcolourbalance(struct gspca_dev *gspca_dev) return -EINVAL; } -int command_setcompressiontarget(struct gspca_dev *gspca_dev) +static int command_setcompressiontarget(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -983,7 +983,7 @@ int command_setcompressiontarget(struct gspca_dev *gspca_dev) sd->params.compressionTarget.targetQ, 0); } -int command_setyuvtresh(struct gspca_dev *gspca_dev) +static int command_setyuvtresh(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -992,7 +992,7 @@ int command_setyuvtresh(struct gspca_dev *gspca_dev) sd->params.yuvThreshold.uvThreshold, 0, 0); } -int command_setcompressionparams(struct gspca_dev *gspca_dev) +static int command_setcompressionparams(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1009,7 +1009,7 @@ int command_setcompressionparams(struct gspca_dev *gspca_dev) sd->params.compressionParams.decimationThreshMod); } -int command_setcompression(struct gspca_dev *gspca_dev) +static int command_setcompression(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1018,7 +1018,7 @@ int command_setcompression(struct gspca_dev *gspca_dev) sd->params.compression.decimation, 0, 0); } -int command_setsensorfps(struct gspca_dev *gspca_dev) +static int command_setsensorfps(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1027,7 +1027,7 @@ int command_setsensorfps(struct gspca_dev *gspca_dev) sd->params.sensorFps.baserate, 0, 0); } -int command_setflickerctrl(struct gspca_dev *gspca_dev) +static int command_setflickerctrl(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1038,7 +1038,7 @@ int command_setflickerctrl(struct gspca_dev *gspca_dev) 0); } -int command_setecptiming(struct gspca_dev *gspca_dev) +static int command_setecptiming(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1046,12 +1046,12 @@ int command_setecptiming(struct gspca_dev *gspca_dev) sd->params.ecpTiming, 0, 0, 0); } -int command_pause(struct gspca_dev *gspca_dev) +static int command_pause(struct gspca_dev *gspca_dev) { return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); } -int command_resume(struct gspca_dev *gspca_dev) +static int command_resume(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1059,7 +1059,8 @@ int command_resume(struct gspca_dev *gspca_dev) 0, sd->params.streamStartLine, 0, 0); } -int command_setlights(struct gspca_dev *gspca_dev) +#if 0 /* Currently unused */ +static int command_setlights(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret, p1, p2; @@ -1078,6 +1079,7 @@ int command_setlights(struct gspca_dev *gspca_dev) return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0, p1 | p2 | 0xE0, 0); } +#endif static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply) { diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 222af479150..efe61593878 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1,7 +1,7 @@ /* * Main USB camera driver * - * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2008-2010 Jean-François Moine <http://moinejf.free.fr> * * Camera button input handling by Márton Németh * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu> @@ -35,12 +35,12 @@ #include <linux/io.h> #include <asm/page.h> #include <linux/uaccess.h> -#include <linux/jiffies.h> +#include <linux/ktime.h> #include <media/v4l2-ioctl.h> #include "gspca.h" -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) #include <linux/input.h> #include <linux/usb/input.h> #endif @@ -51,7 +51,7 @@ #error "DEF_NURBS too big" #endif -MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -115,7 +115,7 @@ static const struct vm_operations_struct gspca_vm_ops = { /* * Input and interrupt endpoint handling functions */ -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static void int_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; @@ -199,7 +199,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, void *buffer = NULL; int ret = -EINVAL; - buffer_len = ep->wMaxPacketSize; + buffer_len = le16_to_cpu(ep->wMaxPacketSize); interval = ep->bInterval; PDEBUG(D_PROBE, "found int in endpoint: 0x%x, " "buffer_len=%u, interval=%u", @@ -213,7 +213,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, goto error; } - buffer = usb_buffer_alloc(dev, ep->wMaxPacketSize, + buffer = usb_buffer_alloc(dev, buffer_len, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { ret = -ENOMEM; @@ -280,9 +280,18 @@ static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) } } #else -#define gspca_input_connect(gspca_dev) 0 -#define gspca_input_create_urb(gspca_dev) -#define gspca_input_destroy_urb(gspca_dev) +static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) +{ +} + +static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev) +{ +} + +static inline int gspca_input_connect(struct gspca_dev *dev) +{ + return 0; +} #endif /* get the current input frame buffer */ @@ -442,8 +451,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { frame->data_end = frame->data; - jiffies_to_timeval(get_jiffies_64(), - &frame->v4l2_buf.timestamp); + 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) @@ -605,6 +613,37 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) } } +static int gspca_set_alt0(struct gspca_dev *gspca_dev) +{ + int ret; + + if (gspca_dev->alt == 0) + return 0; + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); + if (ret < 0) + PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); + return ret; +} + +/* Note: both the queue and the usb locks should be held when calling this */ +static void gspca_stream_off(struct gspca_dev *gspca_dev) +{ + gspca_dev->streaming = 0; + if (gspca_dev->present) { + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); + } + + /* always call stop0 to free the subdriver's resources */ + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + PDEBUG(D_STREAM, "stream off OK"); +} + /* * look for an input transfer endpoint in an alternate setting */ @@ -830,8 +869,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) } if (ret >= 0) break; - gspca_dev->streaming = 0; - destroy_urbs(gspca_dev); + gspca_stream_off(gspca_dev); if (ret != -ENOSPC) { PDEBUG(D_ERR|D_STREAM, "usb_submit_urb alt %d err %d", @@ -861,37 +899,6 @@ out: return ret; } -static int gspca_set_alt0(struct gspca_dev *gspca_dev) -{ - int ret; - - if (gspca_dev->alt == 0) - return 0; - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); - if (ret < 0) - PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); - return ret; -} - -/* Note: both the queue and the usb locks should be held when calling this */ -static void gspca_stream_off(struct gspca_dev *gspca_dev) -{ - gspca_dev->streaming = 0; - if (gspca_dev->present) { - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - } - - /* always call stop0 to free the subdriver's resources */ - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - PDEBUG(D_STREAM, "stream off OK"); -} - static void gspca_set_default_mode(struct gspca_dev *gspca_dev) { int i; @@ -1495,7 +1502,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { struct gspca_dev *gspca_dev = priv; - int i, ret = 0; + int i, ret = 0, streaming; switch (rb->memory) { case GSPCA_MEMORY_READ: /* (internal call) */ @@ -1530,7 +1537,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, } /* stop streaming */ - if (gspca_dev->streaming) { + streaming = gspca_dev->streaming; + if (streaming) { mutex_lock(&gspca_dev->usb_lock); gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); @@ -1549,6 +1557,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (ret == 0) { rb->count = gspca_dev->nframes; gspca_dev->capt_file = file; + if (streaming) + ret = gspca_init_transfer(gspca_dev); } out: mutex_unlock(&gspca_dev->queue_lock); @@ -1582,6 +1592,12 @@ static int vidioc_streamon(struct file *file, void *priv, if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; + /* check the capture file */ + if (gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + if (gspca_dev->nframes == 0 || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { ret = -EINVAL; @@ -1619,6 +1635,12 @@ static int vidioc_streamoff(struct file *file, void *priv, if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; + /* check the capture file */ + if (gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + /* stop streaming */ if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { ret = -ERESTARTSYS; @@ -2124,7 +2146,7 @@ static ssize_t dev_read(struct file *file, char __user *data, } /* get a frame */ - jiffies_to_timeval(get_jiffies_64(), ×tamp); + timestamp = ktime_to_timeval(ktime_get()); timestamp.tv_sec--; n = 2; for (;;) { @@ -2315,7 +2337,7 @@ int gspca_dev_probe(struct usb_interface *intf, return 0; out: -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) if (gspca_dev->input_dev) input_unregister_device(gspca_dev->input_dev); #endif @@ -2334,7 +2356,7 @@ EXPORT_SYMBOL(gspca_dev_probe); void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; #endif @@ -2348,7 +2370,7 @@ void gspca_disconnect(struct usb_interface *intf) wake_up_interruptible(&gspca_dev->wq); } -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) gspca_input_destroy_urb(gspca_dev); input_dev = gspca_dev->input_dev; if (input_dev) { diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 8bb242fb79d..8b963dfae86 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -130,7 +130,7 @@ struct sd_desc { cam_reg_op get_register; #endif cam_ident_op get_chip_ident; -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) cam_int_pkt_op int_pkt_scan; /* other_input makes the gspca core create gspca_dev->input even when int_pkt_scan is NULL, for cams with non interrupt driven buttons */ @@ -158,7 +158,7 @@ struct gspca_dev { struct module *module; /* subdriver handling the device */ struct usb_device *dev; struct file *capt_file; /* file doing video capture */ -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; char phys[64]; /* physical device path */ #endif @@ -171,7 +171,7 @@ struct gspca_dev { #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ struct urb *urb[MAX_NURBS]; -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct urb *int_urb; #endif diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 957e05e2d08..dc1e4efe30f 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -10,8 +10,8 @@ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ * * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr - * PS3 Eye camera, brightness, contrast, hue, AWB control added - * by Max Thrun <bear24rw@gmail.com> + * PS3 Eye camera - brightness, contrast, awb, agc, aec controls + * added by Max Thrun <bear24rw@gmail.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 @@ -60,15 +60,13 @@ struct sd { u8 contrast; u8 gain; u8 exposure; - u8 redblc; - u8 blueblc; - u8 hue; - u8 autogain; + u8 agc; u8 awb; + u8 aec; s8 sharpness; u8 hflip; u8 vflip; - + u8 freqfltr; }; /* V4L2 controls supported by the driver */ @@ -76,197 +74,183 @@ 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_setredblc(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getblueblc(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_setagc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getagc(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_sethflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(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_setawb(struct gspca_dev *gspca_dev, __s32 val); static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val); 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_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreqfltr(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[] = { - { /* 0 */ - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, -#define BRIGHTNESS_DEF 20 - .default_value = BRIGHTNESS_DEF, + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 0 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, - { /* 1 */ - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, -#define CONTRAST_DEF 37 - .default_value = CONTRAST_DEF, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 32 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, - { /* 2 */ - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Main Gain", - .minimum = 0, - .maximum = 63, - .step = 1, + { /* 2 */ + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Main Gain", + .minimum = 0, + .maximum = 63, + .step = 1, #define GAIN_DEF 20 - .default_value = GAIN_DEF, + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, }, - .set = sd_setgain, - .get = sd_getgain, - }, - { /* 3 */ - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 255, - .step = 1, + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, #define EXPO_DEF 120 - .default_value = EXPO_DEF, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, - { /* 4 */ - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 255, - .step = 1, -#define RED_BALANCE_DEF 128 - .default_value = RED_BALANCE_DEF, + .default_value = EXPO_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, }, - .set = sd_setredblc, - .get = sd_getredblc, - }, - { /* 5 */ - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 255, - .step = 1, -#define BLUE_BALANCE_DEF 128 - .default_value = BLUE_BALANCE_DEF, + { /* 4 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AGC_DEF 1 + .default_value = AGC_DEF, + }, + .set = sd_setagc, + .get = sd_getagc, }, - .set = sd_setblueblc, - .get = sd_getblueblc, - }, - { /* 6 */ - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = 255, - .step = 1, -#define HUE_DEF 143 - .default_value = HUE_DEF, +#define AWB_IDX 5 + { /* 5 */ + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AWB_DEF 1 + .default_value = AWB_DEF, + }, + .set = sd_setawb, + .get = sd_getawb, }, - .set = sd_sethue, - .get = sd_gethue, - }, - { /* 7 */ - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Autogain", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AUTOGAIN_DEF 0 - .default_value = AUTOGAIN_DEF, + { /* 6 */ + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AEC_DEF 1 + .default_value = AEC_DEF, + }, + .set = sd_setaec, + .get = sd_getaec, }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -#define AWB_IDX 8 - { /* 8 */ - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AWB_DEF 0 - .default_value = AWB_DEF, - }, - .set = sd_setawb, - .get = sd_getawb, - }, - { /* 9 */ - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 63, - .step = 1, + { /* 7 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 63, + .step = 1, #define SHARPNESS_DEF 0 - .default_value = SHARPNESS_DEF, + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, }, - .set = sd_setsharpness, - .get = sd_getsharpness, - }, - { /* 10 */ - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "HFlip", - .minimum = 0, - .maximum = 1, - .step = 1, + { /* 8 */ + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "HFlip", + .minimum = 0, + .maximum = 1, + .step = 1, #define HFLIP_DEF 0 - .default_value = HFLIP_DEF, + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, }, - .set = sd_sethflip, - .get = sd_gethflip, - }, - { /* 11 */ - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "VFlip", - .minimum = 0, - .maximum = 1, - .step = 1, + { /* 9 */ + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "VFlip", + .minimum = 0, + .maximum = 1, + .step = 1, #define VFLIP_DEF 0 - .default_value = VFLIP_DEF, + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, + { /* 10 */ + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light Frequency Filter", + .minimum = 0, + .maximum = 1, + .step = 1, +#define FREQFLTR_DEF 0 + .default_value = FREQFLTR_DEF, + }, + .set = sd_setfreqfltr, + .get = sd_getfreqfltr, }, - .set = sd_setvflip, - .get = sd_getvflip, - }, }; static const struct v4l2_pix_format ov772x_mode[] = { @@ -675,14 +659,14 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x9B, sd->brightness); + sccb_reg_write(gspca_dev, 0x9b, sd->brightness); } static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x9C, sd->contrast); + sccb_reg_write(gspca_dev, 0x9c, sd->contrast); } static void setgain(struct gspca_dev *gspca_dev) @@ -690,6 +674,9 @@ static void setgain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; + if (sd->agc) + return; + val = sd->gain; switch (val & 0x30) { case 0x00: @@ -717,55 +704,68 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; + if (sd->aec) + return; + + /* 'val' is one byte and represents half of the exposure value we are + * going to set into registers, a two bytes value: + * + * MSB: ((u16) val << 1) >> 8 == val >> 7 + * LSB: ((u16) val << 1) & 0xff == val << 1 + */ val = sd->exposure; sccb_reg_write(gspca_dev, 0x08, val >> 7); sccb_reg_write(gspca_dev, 0x10, val << 1); } -static void setredblc(struct gspca_dev *gspca_dev) +static void setagc(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x43, sd->redblc); -} - -static void setblueblc(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sccb_reg_write(gspca_dev, 0x42, sd->blueblc); -} - -static void sethue(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; + if (sd->agc) { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x04); + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) | 0x03); + } else { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x04); + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) & ~0x03); - sccb_reg_write(gspca_dev, 0x01, sd->hue); + setgain(gspca_dev); + } } -static void setautogain(struct gspca_dev *gspca_dev) +static void setawb(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->autogain) { - sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */ - sccb_reg_write(gspca_dev, 0x64, - sccb_reg_read(gspca_dev, 0x64) | 0x03); + if (sd->awb) { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x02); + sccb_reg_write(gspca_dev, 0x63, + sccb_reg_read(gspca_dev, 0x63) | 0xc0); } else { - sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */ - sccb_reg_write(gspca_dev, 0x64, - sccb_reg_read(gspca_dev, 0x64) & 0xfc); + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x02); + sccb_reg_write(gspca_dev, 0x63, + sccb_reg_read(gspca_dev, 0x63) & ~0xc0); } } -static void setawb(struct gspca_dev *gspca_dev) +static void setaec(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->awb) - sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */ - else - sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */ + if (sd->aec) + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x01); + else { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x01); + setexposure(gspca_dev); + } } static void setsharpness(struct gspca_dev *gspca_dev) @@ -774,8 +774,8 @@ static void setsharpness(struct gspca_dev *gspca_dev) u8 val; val = sd->sharpness; - sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */ - sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */ + sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */ + sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */ } static void sethflip(struct gspca_dev *gspca_dev) @@ -787,7 +787,7 @@ static void sethflip(struct gspca_dev *gspca_dev) sccb_reg_read(gspca_dev, 0x0c) | 0x40); else sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) & 0xbf); + sccb_reg_read(gspca_dev, 0x0c) & ~0x40); } static void setvflip(struct gspca_dev *gspca_dev) @@ -799,9 +799,20 @@ static void setvflip(struct gspca_dev *gspca_dev) sccb_reg_read(gspca_dev, 0x0c) | 0x80); else sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) & 0x7f); + sccb_reg_read(gspca_dev, 0x0c) & ~0x80); } +static void setfreqfltr(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->freqfltr == 0) + sccb_reg_write(gspca_dev, 0x2b, 0x00); + else + sccb_reg_write(gspca_dev, 0x2b, 0x9e); +} + + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -825,26 +836,17 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->contrast = CONTRAST_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPO_DEF; - sd->redblc = RED_BALANCE_DEF; - sd->blueblc = BLUE_BALANCE_DEF; - sd->hue = HUE_DEF; -#if AUTOGAIN_DEF != 0 - sd->autogain = AUTOGAIN_DEF; +#if AGC_DEF != 0 + sd->agc = AGC_DEF; #else gspca_dev->ctrl_inac |= (1 << AWB_IDX); #endif -#if AWB_DEF != 0 - sd->awb = AWB_DEF -#endif -#if SHARPNESS_DEF != 0 + sd->awb = AWB_DEF; + sd->aec = AEC_DEF; sd->sharpness = SHARPNESS_DEF; -#endif -#if HFLIP_DEF != 0 sd->hflip = HFLIP_DEF; -#endif -#if VFLIP_DEF != 0 sd->vflip = VFLIP_DEF; -#endif + sd->freqfltr = FREQFLTR_DEF; return 0; } @@ -904,18 +906,17 @@ static int sd_start(struct gspca_dev *gspca_dev) } set_frame_rate(gspca_dev); - setautogain(gspca_dev); + setagc(gspca_dev); setawb(gspca_dev); + setaec(gspca_dev); setgain(gspca_dev); - setredblc(gspca_dev); - setblueblc(gspca_dev); - sethue(gspca_dev); setexposure(gspca_dev); setbrightness(gspca_dev); setcontrast(gspca_dev); setsharpness(gspca_dev); setvflip(gspca_dev); sethflip(gspca_dev); + setfreqfltr(gspca_dev); ov534_set_led(gspca_dev, 1); ov534_reg_write(gspca_dev, 0xe0, 0x00); @@ -1092,65 +1093,11 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->redblc = val; - if (gspca_dev->streaming) - setredblc(gspca_dev); - return 0; -} - -static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->redblc; - return 0; -} - -static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->blueblc = val; - if (gspca_dev->streaming) - setblueblc(gspca_dev); - return 0; -} - -static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->blueblc; - 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 0; -} - -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_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; + sd->agc = val; if (gspca_dev->streaming) { @@ -1160,16 +1107,16 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) gspca_dev->ctrl_inac &= ~(1 << AWB_IDX); else gspca_dev->ctrl_inac |= (1 << AWB_IDX); - setautogain(gspca_dev); + setagc(gspca_dev); } return 0; } -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->autogain; + *val = sd->agc; return 0; } @@ -1191,6 +1138,24 @@ static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->aec = val; + if (gspca_dev->streaming) + setaec(gspca_dev); + return 0; +} + +static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->aec; + return 0; +} + static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1245,6 +1210,43 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freqfltr = val; + if (gspca_dev->streaming) + setfreqfltr(gspca_dev); + return 0; +} + +static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freqfltr; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "Disabled"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + } + break; + } + + return -EINVAL; +} + /* get stream parameters (framerate) */ static int sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_streamparm *parm) @@ -1296,6 +1298,7 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, .get_streamparm = sd_get_streamparm, .set_streamparm = sd_set_streamparm, }; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 0c87c3490b1..a40f8893310 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -99,7 +99,7 @@ static const struct ctrl sd_ctrls[] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", + .name = "Exposure", .minimum = PAC207_EXPOSURE_MIN, .maximum = PAC207_EXPOSURE_MAX, .step = 1, @@ -130,7 +130,7 @@ static const struct ctrl sd_ctrls[] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", + .name = "Gain", .minimum = PAC207_GAIN_MIN, .maximum = PAC207_GAIN_MAX, .step = 1, diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c index dda5fd4aa69..71d9447a798 100644 --- a/drivers/media/video/gspca/sn9c2028.c +++ b/drivers/media/video/gspca/sn9c2028.c @@ -39,7 +39,7 @@ struct init_command { }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { }; /* How to change the resolution of any of the VGA cams is unknown */ diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 3dee3e5844b..644a7fd4701 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -18,10 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV -#include <linux/kthread.h> -#include <linux/freezer.h> -#include <linux/usb/input.h> +#ifdef CONFIG_INPUT #include <linux/input.h> #include <linux/slab.h> #endif @@ -30,6 +27,7 @@ #include "jpeg.h" #include <media/v4l2-chip-ident.h> +#include <linux/dmi.h> MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, " "microdia project <microdia@googlegroups.com>"); @@ -52,9 +50,15 @@ MODULE_LICENSE("GPL"); #define SENSOR_MT9V112 7 #define SENSOR_MT9M001 8 #define SENSOR_MT9M111 9 -#define SENSOR_HV7131R 10 +#define SENSOR_MT9M112 10 +#define SENSOR_HV7131R 11 #define SENSOR_MT9VPRB 20 +/* camera flags */ +#define HAS_NO_BUTTON 0x1 +#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ +#define FLIP_DETECT 0x4 + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; @@ -88,11 +92,7 @@ struct sd { u8 *jpeg_hdr; u8 quality; -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV - struct input_dev *input_dev; - u8 input_gpio; - struct task_struct *input_task; -#endif + u8 flags; }; struct i2c_reg_u8 { @@ -130,6 +130,39 @@ static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); +static const struct dmi_system_id flip_dmi_table[] = { + { + .ident = "MSI MS-1034", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0341") + } + }, + { + .ident = "MSI MS-1632", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1632") + } + }, + { + .ident = "MSI MS-1635X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1635X") + } + }, + { + .ident = "ASUSTeK W7J", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "W7J ") + } + }, + {} +}; + static const struct ctrl sd_ctrls[] = { { #define BRIGHTNESS_IDX 0 @@ -713,6 +746,7 @@ static u16 i2c_ident[] = { V4L2_IDENT_MT9V112, V4L2_IDENT_MT9M001C12ST, V4L2_IDENT_MT9M111, + V4L2_IDENT_MT9M112, V4L2_IDENT_HV7131R, }; @@ -735,7 +769,8 @@ static u16 bridge_init[][2] = { {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, - {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80} + {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80}, + {0x1007, 0x00} }; /* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ @@ -914,40 +949,30 @@ static struct i2c_reg_u8 ov9650_init[] = { }; static struct i2c_reg_u8 ov9655_init[] = { - {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, - {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, - {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, - {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x32, 0xbf}, - {0x34, 0x3d}, {0x35, 0x00}, {0x36, 0xf8}, {0x38, 0x12}, - {0x39, 0x57}, {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, - {0x3d, 0x19}, {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, - {0x42, 0x80}, {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, - {0x48, 0x3c}, {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, - {0x4d, 0xdc}, {0x4e, 0xdc}, {0x69, 0x02}, {0x6c, 0x04}, - {0x6f, 0x9e}, {0x70, 0x05}, {0x71, 0x78}, {0x77, 0x02}, - {0x8a, 0x23}, {0x8c, 0x0d}, {0x90, 0x7e}, {0x91, 0x7c}, - {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, {0xa6, 0x60}, - {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, {0xab, 0x04}, - {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80}, - {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, {0xb6, 0xaf}, - {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, {0xbe, 0x3b}, - {0xbf, 0x3a}, {0xc0, 0xe2}, {0xc1, 0xc8}, {0xc2, 0x01}, + {0x12, 0x80}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, + {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, + {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, + {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57}, + {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19}, + {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80}, + {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c}, + {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc}, + {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05}, + {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e}, + {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, + {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, + {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, + {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, + {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, + {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01}, {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, - {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x12, 0x61}, + {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, - {0x03, 0x12}, {0x17, 0x14}, {0x18, 0x00}, {0x19, 0x01}, - {0x1a, 0x3d}, {0x32, 0xbf}, {0x11, 0x80}, {0x2a, 0x10}, - {0x2b, 0x0a}, {0x92, 0x00}, {0x93, 0x00}, {0x1e, 0x04}, - {0x1e, 0x04}, {0x10, 0x7c}, {0x04, 0x03}, {0xa1, 0x00}, - {0x2d, 0x00}, {0x2e, 0x00}, {0x00, 0x00}, {0x01, 0x80}, - {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, - {0xc0, 0xaa}, {0x69, 0x0a}, {0x03, 0x12}, {0x17, 0x14}, - {0x18, 0x00}, {0x19, 0x01}, {0x1a, 0x3d}, {0x32, 0xbf}, - {0x11, 0x80}, {0x2a, 0x10}, {0x2b, 0x0a}, {0x92, 0x00}, - {0x93, 0x00}, {0x04, 0x01}, {0x10, 0x1f}, {0xa1, 0x00}, - {0x00, 0x0a}, {0xa1, 0x00}, {0x10, 0x5d}, {0x04, 0x03}, - {0x00, 0x01}, {0xa1, 0x00}, {0x10, 0x7c}, {0x04, 0x03}, - {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, + {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01}, + {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a}, + {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c}, + {0x04, 0x03}, {0x00, 0x13}, }; static struct i2c_reg_u16 mt9v112_init[] = { @@ -971,29 +996,12 @@ static struct i2c_reg_u16 mt9v112_init[] = { static struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, - {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, - {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, - {0x21, 0x0000}, {0x25, 0x4024}, {0x26, 0xff03}, - {0x27, 0xff10}, {0x2b, 0x7828}, {0x2c, 0xb43c}, - {0x2d, 0xf0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, - {0x67, 0x4010}, {0x06, 0x301e}, {0x08, 0x0480}, - {0x01, 0x0004}, {0x02, 0x0016}, {0x03, 0x01e6}, - {0x04, 0x0286}, {0x05, 0x0004}, {0x06, 0x0000}, - {0x07, 0x3002}, {0x08, 0x0008}, {0x0c, 0x0000}, - {0x0d, 0x0000}, {0x0e, 0x0000}, {0x0f, 0x0000}, - {0x10, 0x0000}, {0x11, 0x0000}, {0x12, 0x00b0}, - {0x13, 0x007c}, {0x14, 0x0000}, {0x15, 0x0000}, - {0x16, 0x0000}, {0x17, 0x0000}, {0x18, 0x0000}, - {0x19, 0x0000}, {0x1a, 0x0000}, {0x1b, 0x0000}, - {0x1c, 0x0000}, {0x1d, 0x0000}, {0x30, 0x0000}, - {0x30, 0x0005}, {0x31, 0x0000}, {0x02, 0x0016}, - {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, - {0x06, 0x0000}, {0x07, 0x3002}, {0x06, 0x002d}, - {0x05, 0x0004}, {0x09, 0x0064}, {0x2b, 0x00a0}, - {0x2c, 0x00a0}, {0x2d, 0x00a0}, {0x2e, 0x00a0}, - {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, - {0x05, 0x0004}, {0x06, 0x002d}, {0x07, 0x3002}, - {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, + {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, + {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, + {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016}, + {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004}, + {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008}, + {0x0e, 0x0008}, {0x20, 0x0000} }; static struct i2c_reg_u16 mt9v011_init[] = { @@ -1043,6 +1051,13 @@ static struct i2c_reg_u16 mt9m111_init[] = { {0xf0, 0x0000}, }; +static struct i2c_reg_u16 mt9m112_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, + {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, + {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, + {0xf0, 0x0000}, +}; + static struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, @@ -1240,8 +1255,8 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev) } /* disable hflip and vflip */ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); - sd->hstart = 0; - sd->vstart = 7; + sd->hstart = 1; + sd->vstart = 2; return 0; } @@ -1337,6 +1352,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) return -ENODEV; } } + gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; @@ -1369,6 +1385,23 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) return -ENODEV; } +static int mt9m112_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) { + if (i2c_w2(gspca_dev, mt9m112_init[i].reg, + mt9m112_init[i].val) < 0) { + err("MT9M112 sensor initialization failed"); + return -ENODEV; + } + } + gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX); + sd->hstart = 0; + sd->vstart = 2; + return 0; +} + static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1421,87 +1454,6 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) return 0; } -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV -static int input_kthread(void *data) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *)data; - struct sd *sd = (struct sd *) gspca_dev; - - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait); - set_freezable(); - for (;;) { - if (kthread_should_stop()) - break; - - if (reg_r(gspca_dev, 0x1005, 1) < 0) - continue; - - input_report_key(sd->input_dev, - KEY_CAMERA, - gspca_dev->usb_buf[0] & sd->input_gpio); - input_sync(sd->input_dev); - - wait_event_freezable_timeout(wait, - kthread_should_stop(), - msecs_to_jiffies(100)); - } - return 0; -} - - -static int sn9c20x_input_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - if (sd->input_gpio == 0) - return 0; - - sd->input_dev = input_allocate_device(); - if (!sd->input_dev) - return -ENOMEM; - - sd->input_dev->name = "SN9C20X Webcam"; - - sd->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s", - gspca_dev->dev->bus->bus_name, - gspca_dev->dev->devpath); - - if (!sd->input_dev->phys) - return -ENOMEM; - - usb_to_input_id(gspca_dev->dev, &sd->input_dev->id); - sd->input_dev->dev.parent = &gspca_dev->dev->dev; - - set_bit(EV_KEY, sd->input_dev->evbit); - set_bit(KEY_CAMERA, sd->input_dev->keybit); - - if (input_register_device(sd->input_dev)) - return -EINVAL; - - sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%s-%s", - gspca_dev->dev->bus->bus_name, - gspca_dev->dev->devpath); - - if (IS_ERR(sd->input_task)) - return -EINVAL; - - return 0; -} - -static void sn9c20x_input_cleanup(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - if (sd->input_task != NULL && !IS_ERR(sd->input_task)) - kthread_stop(sd->input_task); - - if (sd->input_dev != NULL) { - input_unregister_device(sd->input_dev); - kfree(sd->input_dev->phys); - input_free_device(sd->input_dev); - sd->input_dev = NULL; - } -} -#endif - static int set_cmatrix(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1579,17 +1531,26 @@ static int set_redblue(struct gspca_dev *gspca_dev) static int set_hvflip(struct gspca_dev *gspca_dev) { - u8 value, tslb; + u8 value, tslb, hflip, vflip; u16 value2; struct sd *sd = (struct sd *) gspca_dev; + + if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { + hflip = !sd->hflip; + vflip = !sd->vflip; + } else { + hflip = sd->hflip; + vflip = sd->vflip; + } + switch (sd->sensor) { case SENSOR_OV9650: i2c_r1(gspca_dev, 0x1e, &value); value &= ~0x30; tslb = 0x01; - if (sd->hflip) + if (hflip) value |= 0x20; - if (sd->vflip) { + if (vflip) { value |= 0x10; tslb = 0x49; } @@ -1600,28 +1561,29 @@ static int set_hvflip(struct gspca_dev *gspca_dev) case SENSOR_MT9V011: i2c_r2(gspca_dev, 0x20, &value2); value2 &= ~0xc0a0; - if (sd->hflip) + if (hflip) value2 |= 0x8080; - if (sd->vflip) + if (vflip) value2 |= 0x4020; i2c_w2(gspca_dev, 0x20, value2); break; + case SENSOR_MT9M112: case SENSOR_MT9M111: case SENSOR_MT9V112: i2c_r2(gspca_dev, 0x20, &value2); value2 &= ~0x0003; - if (sd->hflip) + if (hflip) value2 |= 0x0002; - if (sd->vflip) + if (vflip) value2 |= 0x0001; i2c_w2(gspca_dev, 0x20, value2); break; case SENSOR_HV7131R: i2c_r1(gspca_dev, 0x01, &value); value &= ~0x03; - if (sd->vflip) + if (vflip) value |= 0x01; - if (sd->hflip) + if (hflip) value |= 0x02; i2c_w1(gspca_dev, 0x01, value); break; @@ -1645,7 +1607,6 @@ static int set_exposure(struct gspca_dev *gspca_dev) break; case SENSOR_MT9M001: case SENSOR_MT9V112: - case SENSOR_MT9V111: case SENSOR_MT9V011: exp[0] |= (3 << 4); exp[2] = 0x09; @@ -1655,9 +1616,9 @@ static int set_exposure(struct gspca_dev *gspca_dev) case SENSOR_HV7131R: exp[0] |= (4 << 4); exp[2] = 0x25; - exp[3] = ((sd->exposure * 0xffffff) / 0xffff) >> 16; - exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; - exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; + exp[3] = (sd->exposure >> 5) & 0xff; + exp[4] = (sd->exposure << 3) & 0xff; + exp[5] = 0; break; default: return 0; @@ -1680,7 +1641,6 @@ static int set_gain(struct gspca_dev *gspca_dev) gain[3] = ov_gain[sd->gain]; break; case SENSOR_MT9V011: - case SENSOR_MT9V111: gain[0] |= (3 << 4); gain[2] = 0x35; gain[3] = micron1_gain[sd->gain] >> 8; @@ -1931,7 +1891,7 @@ static int sd_dbg_g_register(struct gspca_dev *gspca_dev, if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && - sd->sensor <= SENSOR_MT9M111) { + sd->sensor <= SENSOR_MT9M112) { if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) return -EINVAL; } else { @@ -1960,7 +1920,7 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && - sd->sensor <= SENSOR_MT9M111) { + sd->sensor <= SENSOR_MT9M112) { if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) return -EINVAL; } else { @@ -2005,8 +1965,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = (id->driver_info >> 8) & 0xff; sd->i2c_addr = id->driver_info & 0xff; + sd->flags = (id->driver_info >> 16) & 0xff; switch (sd->sensor) { + case SENSOR_MT9M112: case SENSOR_MT9M111: case SENSOR_OV9650: case SENSOR_SOI968: @@ -2039,11 +2001,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = 95; -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV - sd->input_gpio = (id->driver_info >> 16) & 0xff; - if (sn9c20x_input_init(gspca_dev) < 0) - return -ENODEV; -#endif return 0; } @@ -2063,6 +2020,11 @@ static int sd_init(struct gspca_dev *gspca_dev) } } + if (sd->flags & LED_REVERSE) + reg_w1(gspca_dev, 0x1006, 0x00); + else + reg_w1(gspca_dev, 0x1006, 0x20); + if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { err("Device initialization failed"); return -ENODEV; @@ -2103,6 +2065,11 @@ static int sd_init(struct gspca_dev *gspca_dev) return -ENODEV; info("MT9M111 sensor detected"); break; + case SENSOR_MT9M112: + if (mt9m112_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M112 sensor detected"); + break; case SENSOR_MT9M001: if (mt9m001_init_sensor(gspca_dev) < 0) return -ENODEV; @@ -2162,6 +2129,7 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); } break; + case SENSOR_MT9M112: case SENSOR_MT9M111: if (mode & MODE_SXGA) { i2c_w2(gspca_dev, 0xf0, 0x0002); @@ -2243,6 +2211,8 @@ static int sd_start(struct gspca_dev *gspca_dev) set_exposure(gspca_dev); set_hvflip(gspca_dev); + reg_w1(gspca_dev, 0x1007, 0x20); + reg_r(gspca_dev, 0x1061, 1); reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); return 0; @@ -2250,6 +2220,8 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + reg_w1(gspca_dev, 0x1007, 0x00); + reg_r(gspca_dev, 0x1061, 1); reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); } @@ -2343,6 +2315,24 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) do_autoexposure(gspca_dev, avg_lum); } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet */ + int len) /* interrupt packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = -EINVAL; + if (!(sd->flags & HAS_NO_BUTTON) && len == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + return ret; +} +#endif + static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -2409,6 +2399,9 @@ static const struct sd_desc sd_desc = { .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif .dq_callback = sd_dqcallback, #ifdef CONFIG_VIDEO_ADV_DEBUG .set_register = sd_dbg_s_register, @@ -2417,8 +2410,8 @@ static const struct sd_desc sd_desc = { .get_chip_ident = sd_chip_ident, }; -#define SN9C20X(sensor, i2c_addr, button_mask) \ - .driver_info = (button_mask << 16) \ +#define SN9C20X(sensor, i2c_addr, flags) \ + .driver_info = ((flags & 0xff) << 16) \ | (SENSOR_ ## sensor << 8) \ | (i2c_addr) @@ -2426,8 +2419,10 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, 0x10)}, - {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)}, + {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, + (FLIP_DETECT | HAS_NO_BUTTON))}, {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, @@ -2438,6 +2433,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, @@ -2448,6 +2444,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)}, {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, @@ -2467,22 +2465,11 @@ static int sd_probe(struct usb_interface *intf, THIS_MODULE); } -static void sd_disconnect(struct usb_interface *intf) -{ -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - - sn9c20x_input_cleanup(gspca_dev); -#endif - - gspca_disconnect(intf); -} - static struct usb_driver sd_driver = { .name = MODULE_NAME, .id_table = device_table, .probe = sd_probe, - .disconnect = sd_disconnect, + .disconnect = gspca_disconnect, #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 1d61b92f6bf..bb923efb75b 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1,7 +1,7 @@ /* * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver * - * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr> * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ #define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0) -MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -67,20 +67,25 @@ struct sd { #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 u8 sensor; /* Type of image sensor chip */ -#define SENSOR_ADCM1700 0 -#define SENSOR_HV7131R 1 -#define SENSOR_MI0360 2 -#define SENSOR_MO4000 3 -#define SENSOR_MT9V111 4 -#define SENSOR_OM6802 5 -#define SENSOR_OV7630 6 -#define SENSOR_OV7648 7 -#define SENSOR_OV7660 8 -#define SENSOR_PO1030 9 -#define SENSOR_SP80708 10 +enum { + SENSOR_ADCM1700, + SENSOR_GC0307, + SENSOR_HV7131R, + SENSOR_MI0360, + SENSOR_MO4000, + SENSOR_MT9V111, + SENSOR_OM6802, + SENSOR_OV7630, + SENSOR_OV7648, + SENSOR_OV7660, + SENSOR_PO1030, + SENSOR_PO2030N, + SENSOR_SOI768, + SENSOR_SP80708, +} sensors; u8 i2c_addr; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -281,29 +286,60 @@ static const struct ctrl sd_ctrls[] = { }; /* table of the disabled controls */ -static __u32 ctrl_dis[] = { - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX) | - (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */ - (1 << INFRARED_IDX) | (1 << FREQ_IDX), - /* SENSOR_HV7131R 1 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MI0360 2 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MO4000 3 */ - (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MT9V111 4 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_OM6802 5 */ - (1 << INFRARED_IDX), - /* SENSOR_OV7630 6 */ - (1 << INFRARED_IDX), - /* SENSOR_OV7648 7 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_OV7660 8 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_PO1030 9 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_SP80708 10 */ +static const __u32 ctrl_dis[] = { +[SENSOR_ADCM1700] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_GC0307] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_HV7131R] = (1 << INFRARED_IDX) | + (1 << FREQ_IDX), + +[SENSOR_MI0360] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_MO4000] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_MT9V111] = (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_OM6802] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_OV7630] = (1 << INFRARED_IDX), + +[SENSOR_OV7648] = (1 << INFRARED_IDX), + +[SENSOR_OV7660] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX), + +[SENSOR_PO1030] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_PO2030N] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), +[SENSOR_SOI768] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_SP80708] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), }; static const struct v4l2_pix_format cif_mode[] = { @@ -343,7 +379,17 @@ static const u8 sn_adcm1700[0x1c] = { 0x06, 0x00, 0x00, 0x00 }; -/*Data from sn9c102p+hv7131r */ +static const u8 sn_gc0307[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, @@ -401,7 +447,7 @@ static const u8 sn_om6802[0x1c] = { static const u8 sn_ov7630[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -443,6 +489,28 @@ static const u8 sn_po1030[0x1c] = { 0x07, 0x00, 0x00, 0x00 }; +static const u8 sn_po2030n[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_soi768[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + static const u8 sn_sp80708[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, @@ -456,17 +524,20 @@ static const u8 sn_sp80708[0x1c] = { /* sequence specific to the sensors - !! index = SENSOR_xxx */ static const u8 *sn_tb[] = { - sn_adcm1700, - sn_hv7131, - sn_mi0360, - sn_mo4000, - sn_mt9v111, - sn_om6802, - sn_ov7630, - sn_ov7648, - sn_ov7660, - sn_po1030, - sn_sp80708 +[SENSOR_ADCM1700] = sn_adcm1700, +[SENSOR_GC0307] = sn_gc0307, +[SENSOR_HV7131R] = sn_hv7131, +[SENSOR_MI0360] = sn_mi0360, +[SENSOR_MO4000] = sn_mo4000, +[SENSOR_MT9V111] = sn_mt9v111, +[SENSOR_OM6802] = sn_om6802, +[SENSOR_OV7630] = sn_ov7630, +[SENSOR_OV7648] = sn_ov7648, +[SENSOR_OV7660] = sn_ov7660, +[SENSOR_PO1030] = sn_po1030, +[SENSOR_PO2030N] = sn_po2030n, +[SENSOR_SOI768] = sn_soi768, +[SENSOR_SP80708] = sn_sp80708, }; /* default gamma table */ @@ -484,8 +555,13 @@ static const u8 gamma_spec_1[17] = { 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5 }; -/* gamma for sensor SP80708 */ +/* gamma for sensor GC0307 */ static const u8 gamma_spec_2[17] = { + 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab, + 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb +}; +/* gamma for sensor SP80708 */ +static const u8 gamma_spec_3[17] = { 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab, 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6 }; @@ -533,6 +609,58 @@ static const u8 adcm1700_sensor_param1[][8] = { {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10}, {} }; +static const u8 gc0307_sensor_init[][8] = { + {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/ + {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 gc0307_sensor_param1[][8] = { + {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10}, + {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10}, + {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10}, +/*param3*/ + {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10}, + {} +}; + static const u8 hv7131r_sensor_init[][8] = { {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, @@ -767,7 +895,9 @@ static const u8 ov7630_sensor_init[][8] = { {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10}, {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, -/* */ + {} +}; +static const u8 ov7630_sensor_param1[][8] = { {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, /*fixme: + 0x12, 0x04*/ @@ -984,6 +1114,113 @@ static const u8 po1030_sensor_param1[][8] = { {} }; +static const u8 po2030n_sensor_init[][8] = { + {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ + {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ + {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10}, + {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10}, + {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10}, + {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10}, + {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10}, + {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10}, + {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10}, + {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10}, + {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10}, + {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10}, + {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10}, + {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10}, + {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10}, + {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10}, + {} +}; +static const u8 po2030n_sensor_param1[][8] = { + {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */ + {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x16, 0x50, 0x40, 0x49, 0x40, 0x10}, +/*param2*/ + {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x16, 0x52, 0x40, 0x48, 0x00, 0x10}, +/*after start*/ + {0xa1, 0x6e, 0x15, 0x0f, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ + {0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ + {0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10}, + {} +}; + +static const u8 soi768_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */ + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 soi768_sensor_param1[][8] = { + {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10}, +/* */ +/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10}, +/* the next sequence should be used for auto gain */ + {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10}, + /* global gain ? : 07 - change with 0x15 at the end */ + {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */ + {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x2d, 0x00, 0x02, 0x00, 0x00, 0x10}, + /* exposure ? : 0200 - change with 0x1e at the end */ + {} +}; + static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, @@ -1069,18 +1306,21 @@ static const u8 sp80708_sensor_param1[][8] = { {} }; -static const u8 (*sensor_init[11])[8] = { - adcm1700_sensor_init, /* ADCM1700 0 */ - hv7131r_sensor_init, /* HV7131R 1 */ - mi0360_sensor_init, /* MI0360 2 */ - mo4000_sensor_init, /* MO4000 3 */ - mt9v111_sensor_init, /* MT9V111 4 */ - om6802_sensor_init, /* OM6802 5 */ - ov7630_sensor_init, /* OV7630 6 */ - ov7648_sensor_init, /* OV7648 7 */ - ov7660_sensor_init, /* OV7660 8 */ - po1030_sensor_init, /* PO1030 9 */ - sp80708_sensor_init, /* SP80708 10 */ +static const u8 (*sensor_init[])[8] = { +[SENSOR_ADCM1700] = adcm1700_sensor_init, +[SENSOR_GC0307] = gc0307_sensor_init, +[SENSOR_HV7131R] = hv7131r_sensor_init, +[SENSOR_MI0360] = mi0360_sensor_init, +[SENSOR_MO4000] = mo4000_sensor_init, +[SENSOR_MT9V111] = mt9v111_sensor_init, +[SENSOR_OM6802] = om6802_sensor_init, +[SENSOR_OV7630] = ov7630_sensor_init, +[SENSOR_OV7648] = ov7648_sensor_init, +[SENSOR_OV7660] = ov7660_sensor_init, +[SENSOR_PO1030] = po1030_sensor_init, +[SENSOR_PO2030N] = po2030n_sensor_init, +[SENSOR_SOI768] = soi768_sensor_init, +[SENSOR_SP80708] = sp80708_sensor_init, }; /* read <len> bytes to gspca_dev->usb_buf */ @@ -1146,10 +1386,11 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; - PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); + PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val); switch (sd->sensor) { case SENSOR_ADCM1700: - case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */ + case SENSOR_OM6802: + case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ gspca_dev->usb_buf[0] = 0x80 | (2 << 4); break; default: /* i2c command = a1 (400 kHz) */ @@ -1177,6 +1418,8 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) static void i2c_w8(struct gspca_dev *gspca_dev, const u8 *buffer) { + PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..", + buffer[2], buffer[3]); memcpy(gspca_dev->usb_buf, buffer, 8); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -1196,7 +1439,8 @@ static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) switch (sd->sensor) { case SENSOR_ADCM1700: - case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */ + case SENSOR_OM6802: + case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ mode[0] = 0x80 | 0x10; break; default: /* i2c command = 91 (400 kHz) */ @@ -1300,39 +1544,100 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) } } -static void ov7648_probe(struct gspca_dev *gspca_dev) +static void ov7630_probe(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u16 val; /* check ov76xx */ reg_w1(gspca_dev, 0x17, 0x62); reg_w1(gspca_dev, 0x01, 0x08); sd->i2c_addr = 0x21; i2c_r(gspca_dev, 0x0a, 2); - if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */ - PDEBUG(D_PROBE, "Sensor ov%02x%02x", - gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x7628) { /* soi768 */ + sd->sensor = SENSOR_SOI768; +/*fixme: only valid for 0c45:613e?*/ + gspca_dev->cam.input_flags = + V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; + PDEBUG(D_PROBE, "Sensor soi768"); return; } + PDEBUG(D_PROBE, "Sensor ov%04x", val); +} - /* reset */ +static void ov7648_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; reg_w1(gspca_dev, 0x01, 0x29); reg_w1(gspca_dev, 0x17, 0x42); + if ((val & 0xff00) == 0x7600) { /* ov76xx */ + PDEBUG(D_PROBE, "Sensor ov%04x", val); + return; + } /* check po1030 */ reg_w1(gspca_dev, 0x17, 0x62); reg_w1(gspca_dev, 0x01, 0x08); sd->i2c_addr = 0x6e; i2c_r(gspca_dev, 0x00, 2); - if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */ - && gspca_dev->usb_buf[4] == 0x30) { + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x1030) { /* po1030 */ PDEBUG(D_PROBE, "Sensor po1030"); sd->sensor = SENSOR_PO1030; return; } - PDEBUG(D_PROBE, "Unknown sensor %02x%02x", - gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + PDEBUG(D_PROBE, "Unknown sensor %04x", val); +} + +/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */ +static void po2030n_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check gc0307 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + reg_w1(gspca_dev, 0x02, 0x22); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x00, 1); + val = gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); /* reset */ + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x99) { /* gc0307 (?) */ + PDEBUG(D_PROBE, "Sensor gc0307"); + sd->sensor = SENSOR_GC0307; + return; + } + + /* check po2030n */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x0a); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x2030) { + PDEBUG(D_PROBE, "Sensor po2030n"); +/* sd->sensor = SENSOR_PO2030N; */ + } else { + PDEBUG(D_PROBE, "Unknown sensor ID %04x", val); + } } static void bridge_init(struct gspca_dev *gspca_dev, @@ -1355,8 +1660,11 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { + case SENSOR_GC0307: case SENSOR_OV7660: case SENSOR_PO1030: + case SENSOR_PO2030N: + case SENSOR_SOI768: case SENSOR_SP80708: reg9a = reg9a_spec; break; @@ -1377,6 +1685,14 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x42); reg_w1(gspca_dev, 0x01, 0x42); break; + case SENSOR_GC0307: + msleep(50); + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x22); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + msleep(50); + break; case SENSOR_MT9V111: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0x61); @@ -1414,11 +1730,18 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x42); break; case SENSOR_PO1030: + case SENSOR_SOI768: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x60); reg_w1(gspca_dev, 0x01, 0x40); break; + case SENSOR_PO2030N: + 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: @@ -1523,9 +1846,15 @@ static int sd_init(struct gspca_dev *gspca_dev) case SENSOR_MI0360: mi0360_probe(gspca_dev); break; + case SENSOR_OV7630: + ov7630_probe(gspca_dev); + break; case SENSOR_OV7648: ov7648_probe(gspca_dev); break; + case SENSOR_PO2030N: + po2030n_probe(gspca_dev); + break; } regGpio[1] = 0x70; reg_w(gspca_dev, 0x01, regGpio, 2); @@ -1558,6 +1887,18 @@ static u32 setexposure(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { + case SENSOR_GC0307: { + int a, b; + + /* expo = 0..255 -> a = 19..43 */ + a = 19 + expo * 25 / 256; + i2c_w1(gspca_dev, 0x68, a); + a -= 12; + b = a * a * 4; /* heuristic */ + i2c_w1(gspca_dev, 0x03, b >> 8); + i2c_w1(gspca_dev, 0x04, b); + break; + } case SENSOR_HV7131R: { u8 Expodoit[] = { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; @@ -1668,6 +2009,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = sd->brightness >> 4; sd->exposure = setexposure(gspca_dev, expo); break; + case SENSOR_GC0307: case SENSOR_MT9V111: expo = sd->brightness >> 8; sd->exposure = setexposure(gspca_dev, expo); @@ -1703,7 +2045,7 @@ static void setcolors(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i, v; u8 reg8a[12]; /* U & V gains */ - static s16 uv[6] = { /* same as reg84 in signed decimal */ + static const s16 uv[6] = { /* same as reg84 in signed decimal */ -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; @@ -1744,9 +2086,12 @@ static void setgamma(struct gspca_dev *gspca_dev) case SENSOR_MT9V111: gamma_base = gamma_spec_1; break; - case SENSOR_SP80708: + case SENSOR_GC0307: gamma_base = gamma_spec_2; break; + case SENSOR_SP80708: + gamma_base = gamma_spec_3; + break; default: gamma_base = gamma_def; break; @@ -1937,14 +2282,17 @@ static int sd_start(struct gspca_dev *gspca_dev) static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; static const u8 CA_adcm1700[] = { 0x14, 0xec, 0x0a, 0xf6 }; + static const u8 CA_po2030n[] = + { 0x1e, 0xe2, 0x14, 0xec }; static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static const u8 CE_gc0307[] = + { 0x32, 0xce, 0x2d, 0xd3 }; static const u8 CE_ov76xx[] = { 0x32, 0xdd, 0x32, 0xdd }; + static const u8 CE_po2030n[] = + { 0x14, 0xe7, 0x1e, 0xdd }; /* 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); @@ -1996,6 +2344,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); switch (sd->sensor) { + case SENSOR_GC0307: + reg17 = 0xa2; + break; case SENSOR_MT9V111: reg17 = 0xe0; break; @@ -2007,9 +2358,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0x20; break; case SENSOR_OV7660: + case SENSOR_SOI768: reg17 = 0xa0; break; case SENSOR_PO1030: + case SENSOR_PO2030N: reg17 = 0xa0; break; default: @@ -2034,12 +2387,18 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_SP80708: reg_w1(gspca_dev, 0x9a, 0x05); break; + case SENSOR_GC0307: case SENSOR_MT9V111: reg_w1(gspca_dev, 0x9a, 0x07); break; + case SENSOR_OV7630: case SENSOR_OV7648: reg_w1(gspca_dev, 0x9a, 0x0a); break; + case SENSOR_PO2030N: + case SENSOR_SOI768: + reg_w1(gspca_dev, 0x9a, 0x06); + break; default: reg_w1(gspca_dev, 0x9a, 0x08); break; @@ -2064,6 +2423,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg1 = 0x46; reg17 = 0xe2; break; + case SENSOR_GC0307: + init = gc0307_sensor_param1; + reg17 = 0xa2; + reg1 = 0x44; + break; case SENSOR_MO4000: if (mode) { /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ @@ -2087,6 +2451,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0x64; /* 640 MCKSIZE */ break; case SENSOR_OV7630: + init = ov7630_sensor_param1; reg17 = 0xe2; reg1 = 0x44; break; @@ -2113,6 +2478,16 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0xa2; reg1 = 0x44; break; + case SENSOR_PO2030N: + init = po2030n_sensor_param1; + reg1 = 0x46; + reg17 = 0xa2; + break; + case SENSOR_SOI768: + init = soi768_sensor_param1; + reg1 = 0x44; + reg17 = 0xa2; + break; default: /* case SENSOR_SP80708: */ init = sp80708_sensor_param1; @@ -2132,17 +2507,33 @@ static int sd_start(struct gspca_dev *gspca_dev) } reg_w(gspca_dev, 0xc0, C0, 6); - if (sd->sensor == SENSOR_ADCM1700) + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_GC0307: + case SENSOR_SOI768: reg_w(gspca_dev, 0xca, CA_adcm1700, 4); - else + break; + case SENSOR_PO2030N: + reg_w(gspca_dev, 0xca, CA_po2030n, 4); + break; + default: reg_w(gspca_dev, 0xca, CA, 4); + break; + } switch (sd->sensor) { case SENSOR_ADCM1700: case SENSOR_OV7630: case SENSOR_OV7648: case SENSOR_OV7660: + case SENSOR_SOI768: reg_w(gspca_dev, 0xce, CE_ov76xx, 4); break; + case SENSOR_GC0307: + reg_w(gspca_dev, 0xce, CE_gc0307, 4); + break; + case SENSOR_PO2030N: + reg_w(gspca_dev, 0xce, CE_po2030n, 4); + break; default: reg_w(gspca_dev, 0xce, CE, 4); /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ @@ -2161,6 +2552,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setvflip(sd); setbrightness(gspca_dev); setcontrast(gspca_dev); + setcolors(gspca_dev); setautogain(gspca_dev); setfreq(gspca_dev); return 0; @@ -2175,11 +2567,16 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; static const u8 stopov7648[] = { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; + static const u8 stopsoi768[] = + { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 }; u8 data; const u8 *sn9c1xx; data = 0x0b; switch (sd->sensor) { + case SENSOR_GC0307: + data = 0x29; + break; case SENSOR_HV7131R: i2c_w8(gspca_dev, stophv7131); data = 0x2b; @@ -2196,6 +2593,10 @@ static void sd_stopN(struct gspca_dev *gspca_dev) case SENSOR_PO1030: data = 0x29; break; + case SENSOR_SOI768: + i2c_w8(gspca_dev, stopsoi768); + data = 0x29; + break; } sn9c1xx = sn_tb[sd->sensor]; reg_w1(gspca_dev, 0x01, sn9c1xx[1]); @@ -2206,13 +2607,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* reg_w1(gspca_dev, 0xf1, 0x01); */ } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2233,6 +2627,14 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (delta < luma_mean - luma_delta || delta > luma_mean + luma_delta) { switch (sd->sensor) { + case SENSOR_GC0307: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + break; case SENSOR_HV7131R: expotimes = sd->exposure >> 8; expotimes += (luma_mean - delta) >> 4; @@ -2584,7 +2986,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, .dq_callback = do_autogain, .get_jcomp = sd_get_jcomp, @@ -2656,7 +3057,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, -/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/ diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index b9c80e2103b..7bb2355005d 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -22,6 +22,7 @@ #define MODULE_NAME "spca561" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -249,9 +250,9 @@ static const __u16 Pb100_2map8300[][2] = { }; static const __u16 spca561_161rev12A_data1[][2] = { - {0x29, 0x8118}, /* white balance - was 21 */ - {0x08, 0x8114}, /* white balance - was 01 */ - {0x0e, 0x8112}, /* white balance - was 00 */ + {0x29, 0x8118}, /* Control register (various enable bits) */ + {0x08, 0x8114}, /* GPIO: Led off */ + {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */ {0x00, 0x8102}, /* white balance - new */ {0x92, 0x8804}, {0x04, 0x8802}, /* windows uses 08 */ @@ -263,15 +264,11 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0x07, 0x8601}, {0x07, 0x8602}, {0x04, 0x8501}, - {0x21, 0x8118}, {0x07, 0x8201}, /* windows uses 02 */ {0x08, 0x8200}, {0x01, 0x8200}, - {0x00, 0x8114}, - {0x01, 0x8114}, /* windows uses 00 */ - {0x90, 0x8604}, {0x00, 0x8605}, {0xb0, 0x8603}, @@ -302,6 +299,9 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0xf0, 0x8505}, {0x32, 0x850a}, /* {0x99, 0x8700}, * - white balance - new (removed) */ + /* HDG we used to do this in stop0, making the init state and the state + after a start / stop different, so do this here instead. */ + {0x29, 0x8118}, {} }; @@ -645,6 +645,9 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) setwhite(gspca_dev); setgain(gspca_dev); setexposure(gspca_dev); + + /* Led ON (bit 3 -> 0 */ + reg_w_val(gspca_dev->dev, 0x8114, 0x00); return 0; } static int sd_start_72a(struct gspca_dev *gspca_dev) @@ -691,26 +694,14 @@ static void sd_stopN(struct gspca_dev *gspca_dev) if (sd->chip_revision == Rev012A) { reg_w_val(gspca_dev->dev, 0x8112, 0x0e); + /* Led Off (bit 3 -> 1 */ + reg_w_val(gspca_dev->dev, 0x8114, 0x08); } else { reg_w_val(gspca_dev->dev, 0x8112, 0x20); /* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ } } -/* 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; - - if (!gspca_dev->present) - return; - if (sd->chip_revision == Rev012A) { - reg_w_val(gspca_dev->dev, 0x8118, 0x29); - reg_w_val(gspca_dev->dev, 0x8114, 0x08); - } -/* reg_w_val(gspca_dev->dev, 0x8114, 0); */ -} - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -788,6 +779,23 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (*data++) { /* sequence number */ case 0: /* start of frame */ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* This should never happen */ + if (len < 2) { + PDEBUG(D_ERR, "Short SOF packet, ignoring"); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + +#ifdef CONFIG_INPUT + if (data[0] & 0x20) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } +#endif + if (data[1] & 0x10) { /* compressed bayer */ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); @@ -1028,8 +1036,10 @@ static const struct sd_desc sd_desc_12a = { .init = sd_init_12a, .start = sd_start_12a, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, +#ifdef CONFIG_INPUT + .other_input = 1, +#endif }; static const struct sd_desc sd_desc_72a = { .name = MODULE_NAME, @@ -1039,9 +1049,11 @@ static const struct sd_desc sd_desc_72a = { .init = sd_init_72a, .start = sd_start_72a, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, +#ifdef CONFIG_INPUT + .other_input = 1, +#endif }; static const struct sd_desc *sd_desc[2] = { &sd_desc_12a, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 668a7536af9..63014372adb 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -44,7 +44,10 @@ struct sd { u8 gamma; u8 sharpness; u8 freq; - u8 whitebalance; + u8 red_balance; /* split balance */ + u8 blue_balance; + u8 global_gain; /* aka gain */ + u8 whitebalance; /* set default r/g/b and activate */ u8 mirror; u8 effect; @@ -70,8 +73,17 @@ 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_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_seteffect(struct gspca_dev *gspca_dev, __s32 val); @@ -79,6 +91,7 @@ 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[] = { { { @@ -139,7 +152,7 @@ static const struct ctrl sd_ctrls[] = { }, { { - .id = V4L2_CID_GAIN, /* here, i activate only the lowlight, + .id = V4L2_CID_BACKLIGHT_COMPENSATION, /* Activa lowlight, * some apps dont bring up the * backligth_compensation control) */ .type = V4L2_CTRL_TYPE_INTEGER, @@ -183,7 +196,7 @@ static const struct ctrl sd_ctrls[] = { { { - .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "White Balance", .minimum = 0, @@ -223,6 +236,48 @@ static const struct ctrl sd_ctrls[] = { .set = sd_seteffect, .get = sd_geteffect }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define BLUE_BALANCE_DEF 0x20 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblue_balance, + .get = sd_getblue_balance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define RED_BALANCE_DEF 0x20 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setred_balance, + .get = sd_getred_balance, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define global_gain_DEF 0x20 + .default_value = global_gain_DEF, + }, + .set = sd_setglobal_gain, + .get = sd_getglobal_gain, + }, }; static char *effects_control[] = { @@ -523,6 +578,10 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, u8 *tmpbuf; tmpbuf = kmalloc(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), @@ -542,10 +601,15 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev, int i; u8 *p, *tmpbuf; - if (len * 2 <= USB_BUF_SZ) + if (len * 2 <= USB_BUF_SZ) { p = tmpbuf = gspca_dev->usb_buf; - else + } else { p = tmpbuf = kmalloc(len * 2, GFP_KERNEL); + if (!tmpbuf) { + err("Out of memory"); + return; + } + } i = len; while (--i >= 0) { *p++ = reg++; @@ -642,6 +706,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->whitebalance = WHITE_BALANCE_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; + return 0; } @@ -693,18 +761,40 @@ 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 setwhitebalance(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); +} + +/* Generic fnc for r/b balance, exposure and whitebalance */ +static void setbalance(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 white_balance[8] = - {0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38}; + /* on whitebalance leave defaults values */ + if (sd->whitebalance) { + reg_w(gspca_dev, 0x3c80); + } else { + reg_w(gspca_dev, 0x3880); + /* 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); + } + +} + - if (sd->whitebalance) - white_balance[7] = 0x3c; - reg_w_buf(gspca_dev, white_balance, sizeof white_balance); +static void setwhitebalance(struct gspca_dev *gspca_dev) +{ + setbalance(gspca_dev); } static void setsharpness(struct gspca_dev *gspca_dev) @@ -1018,6 +1108,66 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } + +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue_balance = 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) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red_balance = 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) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + + + +static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->global_gain = val; + if (gspca_dev->streaming) + setglobalgain(gspca_dev); + + return 0; +} + +static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->global_gain; + return 0; +} + + static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 4989f9afb46..732c3dfe46f 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -1,9 +1,9 @@ /* - * Z-star vc0321 library - * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl - * Copyright (C) 2006 Michel Xhaard + * Z-star vc0321 library * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr> + * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl + * Copyright (C) 2006 Michel Xhaard * * 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 @@ -24,7 +24,7 @@ #include "gspca.h" -MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -1971,268 +1971,489 @@ static const u8 ov7660_NoFliker[][4] = { {} }; -static const u8 ov7670_initVGA_JPG[][4] = { +static const u8 ov7670_InitVGA[][4] = { {0xb3, 0x01, 0x05, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x00, 0x66, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x16, 0x01, 0xcc}, {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0x41, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, - {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, - {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, - {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, - {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, - {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, - {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, - {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, - {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, - {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, - {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, - {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, - {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, - {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, - {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, - {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, - {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, - {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, - {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xbc, 0x00, 0x41, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, + {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, + {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, + {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, + {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, + {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, + {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, + {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, + {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, + {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, + {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, + {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, + {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, + {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, + {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, + {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, + {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, + {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ {0x00, 0x21, 0x02, 0xaa}, - {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, - {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, - {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, - {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, - {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, - {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, - {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, - {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, - {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, - {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, - {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, - {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, - {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, - {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, - {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, - {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, - {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, - {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, - {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, - {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, - {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, - {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, - {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, - {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, - {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, - {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, - {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, - {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, - {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, - {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, - {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, - {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, - {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, - {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, - {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, - {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, - {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, - {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, - {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, - {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, - {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, - {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, - {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, - {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, - {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, - {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, + {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, + {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, + {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, + {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, + {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, + {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, + {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, + {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, + {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, + {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, + {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, + {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, + {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, + {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, + {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, + {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, + {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, + {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, + {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, + {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, + {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, + {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, + {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, + {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, + {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, + {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, + {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, + {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, + {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, + {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, + {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, + {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, + {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, + {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, + {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, + {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, + {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, + {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, + {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, + {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, + {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, + {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, + {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, + {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, + {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, + {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, - {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, - {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, - {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, - {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x13, 0xcc}, - {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x24, 0x80, 0xaa}, + {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, + {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, + {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, + {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, {0x00, 0x77, 0x05, 0xaa}, {}, }; -static const u8 ov7670_initQVGA_JPG[][4] = { - {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, - {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, - {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, - {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, - {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, - {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, - {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, - {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, - {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, - {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, - {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, - {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, - {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, - {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, - {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, - {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, - {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, - {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, - {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, +static const u8 ov7670_InitQVGA[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x16, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, + {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, + {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, + {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, + {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, + {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, + {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, + {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, + {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, + {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, + {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, + {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, + {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, + {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, + {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, + {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, + {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, + {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ {0x00, 0x21, 0x02, 0xaa}, - {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, - {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, - {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, - {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, - {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, - {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, - {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, - {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, - {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, - {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, - {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, - {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, - {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, - {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, - {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, - {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, - {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, - {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, - {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, - {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, - {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, - {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, - {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, - {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, - {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, - {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, - {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, - {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, - {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, - {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, - {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, - {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, - {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, - {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, - {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, - {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, - {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, - {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, - {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, - {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, - {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, - {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, - {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, - {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, - {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, - {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, + {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, + {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, + {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, + {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, + {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, + {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, + {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, + {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, + {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, + {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, + {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, + {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, + {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, + {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, + {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, + {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, + {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, + {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, + {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, + {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, + {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, + {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, + {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, + {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, + {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, + {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, + {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, + {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, + {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, + {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, + {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, + {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, + {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, + {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, + {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, + {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, + {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, + {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, + {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, + {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, + {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, + {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, + {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, + {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, + {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, + {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, - {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, - {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, - {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x21, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, - {0x00, 0x77, 0x05, 0xaa }, + {0x00, 0x24, 0x80, 0xaa}, + {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, + {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, + {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, + {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, {}, }; @@ -3117,6 +3338,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = bi_mode; cam->nmodes = ARRAY_SIZE(bi_mode); break; + case SENSOR_OV7670: + cam->cam_mode = bi_mode; + cam->nmodes = ARRAY_SIZE(bi_mode) - 1; + break; default: cam->cam_mode = vc0323_mode; cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; @@ -3329,14 +3554,6 @@ static int sd_start(struct gspca_dev *gspca_dev) else init = ov7660_initVGA_data; /* 640x480 */ break; - case SENSOR_OV7670: - /*GammaT = ov7660_gamma; */ - /*MatrixT = ov7660_matrix; */ - if (mode) - init = ov7670_initQVGA_JPG; /* 320x240 */ - else - init = ov7670_initVGA_JPG; /* 640x480 */ - break; case SENSOR_MI0360: GammaT = mi1320_gamma; MatrixT = mi0360_matrix; @@ -3373,6 +3590,9 @@ static int sd_start(struct gspca_dev *gspca_dev) MatrixT = mi1320_matrix; init = mi1320_soc_init[mode]; break; + case SENSOR_OV7670: + init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA; + break; case SENSOR_PO3130NC: GammaT = po3130_gamma; MatrixT = po3130_matrix; @@ -3426,7 +3646,13 @@ static int sd_start(struct gspca_dev *gspca_dev) sethvflip(gspca_dev); setlightfreq(gspca_dev); } - if (sd->sensor == SENSOR_POxxxx) { + 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); + break; + case SENSOR_POxxxx: setcolors(gspca_dev); setbrightness(gspca_dev); setcontrast(gspca_dev); @@ -3435,6 +3661,7 @@ static int sd_start(struct gspca_dev *gspca_dev) msleep(80); reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); usb_exchange(gspca_dev, poxxxx_init_end_2); + break; } return 0; } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 7d7814c43f9..d02aa5c8472 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -40,7 +40,6 @@ 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; @@ -80,8 +79,6 @@ struct sd { }; /* 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); @@ -94,21 +91,6 @@ 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[] = { -#define BRIGHTNESS_IDX 0 - { - { - .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, @@ -150,7 +132,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define LIGHTFREQ_IDX 4 +#define LIGHTFREQ_IDX 3 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -6004,33 +5986,6 @@ static void setmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, matrix[i], 0x010a + i); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 brightness; - - switch (sd->sensor) { - case SENSOR_GC0305: - case SENSOR_OV7620: - case SENSOR_PAS202B: - case SENSOR_PO2030: - return; - } -/*fixme: is it really write to 011d and 018d for all other sensors? */ - brightness = sd->brightness; - reg_w(gspca_dev->dev, brightness, 0x011d); - switch (sd->sensor) { - case SENSOR_ADCM2700: - case SENSOR_HV7131B: - return; - } - if (brightness < 0x70) - brightness += 0x10; - else - brightness = 0x80; - reg_w(gspca_dev->dev, brightness, 0x018d); -} - static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -6059,8 +6014,8 @@ static void setcontrast(struct gspca_dev *gspca_dev) int g, i, k, adj, gp; u8 gr[16]; static const u8 delta_tb[16] = /* delta for contrast */ - {0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02}; static const u8 gamma_tb[6][16] = { {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f, 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}, @@ -6082,11 +6037,11 @@ static void setcontrast(struct gspca_dev *gspca_dev) adj = 0; gp = 0; for (i = 0; i < 16; i++) { - g = Tgamma[i] - delta_tb[i] * k / 128 - adj / 2; + g = Tgamma[i] - delta_tb[i] * k / 256 - adj / 2; if (g > 0xff) g = 0xff; - else if (g <= 0) - g = 1; + else if (g < 0) + g = 0; reg_w(dev, g, 0x0120 + i); /* gamma */ if (k > 0) adj--; @@ -6203,13 +6158,13 @@ static int setlightfreq(struct gspca_dev *gspca_dev) pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, /* SENSOR_PAS202B 13 */ - {pas202b_NoFlikerScale, pas202b_NoFliker, - pas202b_50HZScale, pas202b_50HZ, - pas202b_60HZScale, pas202b_60HZ}, + {pas202b_NoFliker, pas202b_NoFlikerScale, + pas202b_50HZ, pas202b_50HZScale, + pas202b_60HZ, pas202b_60HZScale}, /* SENSOR_PB0330 14 */ - {pb0330_NoFlikerScale, pb0330_NoFliker, - pb0330_50HZScale, pb0330_50HZ, - pb0330_60HZScale, pb0330_60HZ}, + {pb0330_NoFliker, pb0330_NoFlikerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, /* SENSOR_PO2030 15 */ {po2030_NoFliker, po2030_NoFliker, po2030_50HZ, po2030_50HZ, @@ -6789,7 +6744,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(broken_vga_mode); break; } - sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->gamma = gamma[sd->sensor]; sd->autogain = AUTOGAIN_DEF; @@ -6797,12 +6751,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = QUALITY_DEF; switch (sd->sensor) { - case SENSOR_GC0305: - case SENSOR_OV7620: - case SENSOR_PAS202B: - case SENSOR_PO2030: - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); - break; case SENSOR_HV7131B: case SENSOR_HV7131C: case SENSOR_OV7630C: @@ -6893,7 +6841,6 @@ static int sd_start(struct gspca_dev *gspca_dev) } setmatrix(gspca_dev); - setbrightness(gspca_dev); switch (sd->sensor) { case SENSOR_ADCM2700: case SENSOR_OV7620: @@ -7015,24 +6962,6 @@ 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) - setbrightness(gspca_dev); - return 0; -} - -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; diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 2fc9865fd48..830d47b05e1 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -373,9 +373,6 @@ static int hdpvr_probe(struct usb_interface *interface, } #endif /* CONFIG_I2C */ - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - /* let the user know what node this device is now attached to */ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n", video_device_node_name(dev->video_dev)); @@ -391,44 +388,24 @@ error: static void hdpvr_disconnect(struct usb_interface *interface) { - struct hdpvr_device *dev; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); + struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface)); + v4l2_info(&dev->v4l2_dev, "device %s disconnected\n", + video_device_node_name(dev->video_dev)); /* prevent more I/O from starting and stop any ongoing */ mutex_lock(&dev->io_mutex); dev->status = STATUS_DISCONNECTED; - v4l2_device_disconnect(&dev->v4l2_dev); - video_unregister_device(dev->video_dev); wake_up_interruptible(&dev->wait_data); wake_up_interruptible(&dev->wait_buffer); mutex_unlock(&dev->io_mutex); + v4l2_device_disconnect(&dev->v4l2_dev); msleep(100); flush_workqueue(dev->workqueue); mutex_lock(&dev->io_mutex); hdpvr_cancel_queue(dev); - destroy_workqueue(dev->workqueue); mutex_unlock(&dev->io_mutex); - - /* deregister I2C adapter */ -#ifdef CONFIG_I2C - mutex_lock(&dev->i2c_mutex); - if (dev->i2c_adapter) - i2c_del_adapter(dev->i2c_adapter); - kfree(dev->i2c_adapter); - dev->i2c_adapter = NULL; - mutex_unlock(&dev->i2c_mutex); -#endif /* CONFIG_I2C */ - + video_unregister_device(dev->video_dev); atomic_dec(&dev_nr); - - v4l2_info(&dev->v4l2_dev, "device %s disconnected\n", - video_device_node_name(dev->video_dev)); - - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev->usbc_buf); - kfree(dev); } diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 196f82de48f..d2f0ee29737 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -1214,6 +1214,24 @@ static void hdpvr_device_release(struct video_device *vdev) struct hdpvr_device *dev = video_get_drvdata(vdev); hdpvr_delete(dev); + mutex_lock(&dev->io_mutex); + destroy_workqueue(dev->workqueue); + mutex_unlock(&dev->io_mutex); + + v4l2_device_unregister(&dev->v4l2_dev); + + /* deregister I2C adapter */ +#ifdef CONFIG_I2C + mutex_lock(&dev->i2c_mutex); + if (dev->i2c_adapter) + i2c_del_adapter(dev->i2c_adapter); + kfree(dev->i2c_adapter); + dev->i2c_adapter = NULL; + mutex_unlock(&dev->i2c_mutex); +#endif /* CONFIG_I2C */ + + kfree(dev->usbc_buf); + kfree(dev); } static const struct video_device hdpvr_video_template = { diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h index 49ae25d83d1..b0f046df3cd 100644 --- a/drivers/media/video/hdpvr/hdpvr.h +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -111,6 +111,11 @@ struct hdpvr_device { u8 *usbc_buf; }; +static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev); +} + /* buffer one bulk urb of data */ struct hdpvr_buffer { diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index da18d698e7f..29d43974265 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -61,9 +61,9 @@ module_param(hauppauge, int, 0644); /* Choose Hauppauge remote */ MODULE_PARM_DESC(hauppauge, "Specify Hauppauge remote: 0=black, 1=grey (defaults to 0)"); -#define DEVNAME "ir-kbd-i2c" +#define MODULE_NAME "ir-kbd-i2c" #define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG DEVNAME ": " fmt , ## arg) + printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) /* ----------------------------------------------------------------------- */ @@ -297,7 +297,7 @@ static void ir_work(struct work_struct *work) static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; const char *name = NULL; u64 ir_type = 0; struct IR_i2c *ir; @@ -322,13 +322,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "Pixelview"; ir->get_key = get_key_pixelview; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_empty_table; + ir_codes = RC_MAP_EMPTY; break; case 0x4b: name = "PV951"; ir->get_key = get_key_pv951; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_pv951_table; + ir_codes = RC_MAP_PV951; break; case 0x18: case 0x1f: @@ -337,22 +337,22 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->get_key = get_key_haup; ir_type = IR_TYPE_RC5; if (hauppauge == 1) { - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; } else { - ir_codes = &ir_codes_rc5_tv_table; + ir_codes = RC_MAP_RC5_TV; } break; case 0x30: name = "KNC One"; ir->get_key = get_key_knc1; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_empty_table; + ir_codes = RC_MAP_EMPTY; break; case 0x6b: name = "FusionHDTV"; ir->get_key = get_key_fusionhdtv; ir_type = IR_TYPE_RC5; - ir_codes = &ir_codes_fusionhdtv_mce_table; + ir_codes = RC_MAP_FUSIONHDTV_MCE; break; case 0x0b: case 0x47: @@ -365,9 +365,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_RC5; ir->get_key = get_key_haup_xvr; if (hauppauge == 1) { - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; } else { - ir_codes = &ir_codes_rc5_tv_table; + ir_codes = RC_MAP_RC5_TV; } } else { /* Handled by saa7134-input */ @@ -379,7 +379,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "AVerMedia Cardbus remote"; ir->get_key = get_key_avermedia_cardbus; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_avermedia_cardbus_table; + ir_codes = RC_MAP_AVERMEDIA_CARDBUS; break; } @@ -447,11 +447,11 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) input_dev->name = ir->name; input_dev->phys = ir->phys; - err = ir_input_register(ir->input, ir->ir_codes, NULL); + err = ir_input_register(ir->input, ir->ir_codes, NULL, MODULE_NAME); if (err) goto err_out_free; - printk(DEVNAME ": %s detected at %s [%s]\n", + printk(MODULE_NAME ": %s detected at %s [%s]\n", ir->input->name, ir->input->phys, adap->name); /* start polling via eventd */ diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 9a250548be4..1b79475ca13 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -1293,7 +1293,6 @@ int ivtv_init_on_first_open(struct ivtv *itv) ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ivtv_init_mpeg_decoder(itv); } - ivtv_s_std(NULL, &fh, &itv->tuner_std); /* On a cx23416 this seems to be able to enable DMA to the chip? */ if (!itv->has_cx23415) @@ -1310,6 +1309,10 @@ int ivtv_init_on_first_open(struct ivtv *itv) } else ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); + + /* For cards with video out, this call needs interrupts enabled */ + ivtv_s_std(NULL, &fh, &itv->tuner_std); + return 0; } diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 5028e31c564..5b45fd2b264 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -63,6 +63,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> #include <media/tuner.h> #include <media/cx2341x.h> #include <media/ir-kbd-i2c.h> @@ -116,6 +117,9 @@ #define IVTV_REG_VPU (0x9058) #define IVTV_REG_APU (0xA064) +/* Other registers */ +#define IVTV_REG_DEC_LINE_FIELD (0x28C0) + /* debugging */ extern int ivtv_debug; @@ -372,6 +376,7 @@ struct ivtv_stream { }; struct ivtv_open_id { + struct v4l2_fh fh; u32 open_id; /* unique ID for this file descriptor */ int type; /* stream type */ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */ @@ -379,6 +384,11 @@ struct ivtv_open_id { struct ivtv *itv; }; +static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh) +{ + return container_of(fh, struct ivtv_open_id, fh); +} + struct yuv_frame_info { u32 update; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index babcabd73c0..abf410943cc 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 <media/v4l2-event.h> #include <media/saa7115.h> /* This function tries to claim the stream for a specific file descriptor. @@ -506,7 +507,7 @@ int ivtv_start_capture(struct ivtv_open_id *id) ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; int rc; @@ -541,7 +542,7 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; struct yuv_playback_info *yi = &itv->yuv_info; @@ -711,19 +712,31 @@ retry: unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; int res = 0; /* add stream's waitq to the poll list */ IVTV_DEBUG_HI_FILE("Decoder poll\n"); - poll_wait(filp, &s->waitq, wait); - set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); - if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || - test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) - res = POLLPRI; + /* If there are subscribed events, then only use the new event + API instead of the old video.h based API. */ + if (!list_empty(&id->fh.events->subscribed)) { + poll_wait(filp, &id->fh.events->wait, wait); + /* Turn off the old-style vsync events */ + clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (v4l2_event_pending(&id->fh)) + res = POLLPRI; + } else { + /* This is the old-style API which is here only for backwards + compatibility. */ + poll_wait(filp, &s->waitq, wait); + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || + test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + res = POLLPRI; + } /* Allow write if buffers are available for writing */ if (s->q_free.buffers) @@ -733,7 +746,7 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags); @@ -833,13 +846,16 @@ static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts) int ivtv_v4l2_close(struct file *filp) { - struct ivtv_open_id *id = filp->private_data; + struct v4l2_fh *fh = filp->private_data; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; IVTV_DEBUG_FILE("close %s\n", s->name); - v4l2_prio_close(&itv->prio, &id->prio); + v4l2_prio_close(&itv->prio, id->prio); + v4l2_fh_del(fh); + v4l2_fh_exit(fh); /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { @@ -895,6 +911,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) { struct ivtv *itv = s->itv; struct ivtv_open_id *item; + int res = 0; IVTV_DEBUG_FILE("open %s\n", s->name); @@ -915,17 +932,27 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) } /* Allocate memory */ - item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); + item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); if (NULL == item) { IVTV_DEBUG_WARN("nomem on v4l2 open\n"); return -ENOMEM; } + v4l2_fh_init(&item->fh, s->vdev); + if (s->type == IVTV_DEC_STREAM_TYPE_YUV || + s->type == IVTV_DEC_STREAM_TYPE_MPG) { + res = v4l2_event_alloc(&item->fh, 60); + } + if (res < 0) { + v4l2_fh_exit(&item->fh); + kfree(item); + return res; + } item->itv = itv; item->type = s->type; v4l2_prio_open(&itv->prio, &item->prio); item->open_id = itv->open_id++; - filp->private_data = item; + filp->private_data = &item->fh; if (item->type == IVTV_ENC_STREAM_TYPE_RAD) { /* Try to claim this stream */ @@ -940,6 +967,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* switching to radio while capture is in progress is not polite */ ivtv_release_stream(s); + v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; } @@ -970,6 +998,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); itv->yuv_info.stream_size = 0; } + v4l2_fh_add(&item->fh); return 0; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 2ee03c2a1b5..a5b92d109c6 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -193,7 +193,7 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case IVTV_HW_I2C_IR_RX_AVER: - init_data->ir_codes = &ir_codes_avermedia_cardbus_table; + init_data->ir_codes = RC_MAP_AVERMEDIA_CARDBUS; init_data->internal_get_key_func = IR_KBD_GET_KEY_AVERMEDIA_CARDBUS; init_data->type = IR_TYPE_OTHER; @@ -202,14 +202,14 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) case IVTV_HW_I2C_IR_RX_HAUP_EXT: case IVTV_HW_I2C_IR_RX_HAUP_INT: /* Default to old black remote */ - init_data->ir_codes = &ir_codes_rc5_tv_table; + init_data->ir_codes = RC_MAP_RC5_TV; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; init_data->type = IR_TYPE_RC5; init_data->name = itv->card_name; break; case IVTV_HW_Z8F0811_IR_RX_HAUP: /* Default to grey remote */ - init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = IR_TYPE_RC5; init_data->name = itv->card_name; diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 99f3c39a118..fa9f0d958f9 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -35,6 +35,7 @@ #include <media/saa7127.h> #include <media/tveeprom.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-event.h> #include <linux/dvb/audio.h> #include <linux/i2c-id.h> @@ -391,7 +392,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - v4l2_subdev_call(itv->sd_video, video, g_fmt, fmt); + v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } @@ -597,7 +598,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f return -EBUSY; itv->vbi.sliced_in->service_set = 0; itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); + v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi); return ivtv_g_fmt_vbi_cap(file, fh, fmt); } @@ -615,7 +616,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) return -EBUSY; itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); + v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt); memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); return 0; } @@ -1087,8 +1088,10 @@ static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) { + DEFINE_WAIT(wait); struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; struct yuv_playback_info *yi = &itv->yuv_info; + int f; if ((*std & V4L2_STD_ALL) == 0) return -EINVAL; @@ -1128,6 +1131,25 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) itv->is_out_60hz = itv->is_60hz; itv->is_out_50hz = itv->is_50hz; ivtv_call_all(itv, video, s_std_output, itv->std_out); + + /* + * The next firmware call is time sensitive. Time it to + * avoid risk of a hard lock, by trying to ensure the call + * happens within the first 100 lines of the top field. + * Make 4 attempts to sync to the decoder before giving up. + */ + for (f = 0; f < 4; f++) { + prepare_to_wait(&itv->vsync_waitq, &wait, + TASK_UNINTERRUPTIBLE); + if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100) + break; + schedule_timeout(msecs_to_jiffies(25)); + } + finish_wait(&itv->vsync_waitq, &wait); + + if (f == 4) + IVTV_WARN("Mode change failed to sync to decoder\n"); + 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; @@ -1431,6 +1453,18 @@ static int ivtv_overlay(struct file *file, void *fh, unsigned int on) return 0; } +static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_VSYNC: + case V4L2_EVENT_EOS: + break; + default: + return -EINVAL; + } + return v4l2_event_subscribe(fh, sub); +} + static int ivtv_log_status(struct file *file, void *fh) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; @@ -1539,10 +1573,11 @@ static int ivtv_log_status(struct file *file, void *fh) static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) { - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; int nonblocking = filp->f_flags & O_NONBLOCK; struct ivtv_stream *s = &itv->streams[id->type]; + unsigned long iarg = (unsigned long)arg; switch (cmd) { case IVTV_IOC_DMA_FRAME: { @@ -1724,6 +1759,33 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) break; } + case VIDEO_SELECT_SOURCE: + IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX); + + case AUDIO_SET_MUTE: + IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); + itv->speed_mute_audio = iarg; + return 0; + + case AUDIO_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); + if (iarg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_stereo_mode = iarg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + case AUDIO_BILINGUAL_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); + if (iarg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_bilingual_mode = iarg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + default: return -EINVAL; } @@ -1755,6 +1817,10 @@ static long ivtv_default(struct file *file, void *fh, int cmd, void *arg) case VIDEO_CONTINUE: case VIDEO_COMMAND: case VIDEO_TRY_COMMAND: + case VIDEO_SELECT_SOURCE: + case AUDIO_SET_MUTE: + case AUDIO_CHANNEL_SELECT: + case AUDIO_BILINGUAL_CHANNEL_SELECT: return ivtv_decoder_ioctls(file, cmd, (void *)arg); default: @@ -1767,42 +1833,9 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); long ret; - /* Filter dvb ioctls that cannot be handled by the v4l ioctl framework */ - switch (cmd) { - case VIDEO_SELECT_SOURCE: - IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX); - - case AUDIO_SET_MUTE: - IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); - itv->speed_mute_audio = arg; - return 0; - - case AUDIO_CHANNEL_SELECT: - IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); - if (arg > AUDIO_STEREO_SWAPPED) - return -EINVAL; - itv->audio_stereo_mode = arg; - ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); - return 0; - - case AUDIO_BILINGUAL_CHANNEL_SELECT: - IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); - if (arg > AUDIO_STEREO_SWAPPED) - return -EINVAL; - itv->audio_bilingual_mode = arg; - ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); - return 0; - - default: - break; - } - /* check priority */ switch (cmd) { case VIDIOC_S_CTRL: @@ -1817,8 +1850,9 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, case VIDIOC_S_AUDOUT: case VIDIOC_S_EXT_CTRLS: case VIDIOC_S_FBUF: + case VIDIOC_S_PRIORITY: case VIDIOC_OVERLAY: - ret = v4l2_prio_check(&itv->prio, &id->prio); + ret = v4l2_prio_check(&itv->prio, id->prio); if (ret) return ret; } @@ -1832,10 +1866,13 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; long res; + /* DQEVENT can block, so this should not run with the serialize lock */ + if (cmd == VIDIOC_DQEVENT) + return ivtv_serialized_ioctl(itv, filp, cmd, arg); mutex_lock(&itv->serialize_lock); res = ivtv_serialized_ioctl(itv, filp, cmd, arg); mutex_unlock(&itv->serialize_lock); @@ -1906,6 +1943,8 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .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, }; void ivtv_set_funcs(struct video_device *vdev) diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index 12d36ca91d5..fea1ec33b0d 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -25,6 +25,7 @@ #include "ivtv-mailbox.h" #include "ivtv-vbi.h" #include "ivtv-yuv.h" +#include <media/v4l2-event.h> #define DMA_MAGIC_COOKIE 0x000001fe @@ -752,7 +753,7 @@ static void ivtv_irq_vsync(struct ivtv *itv) * to determine the line being displayed and ensure we handle * one vsync per frame. */ - unsigned int frame = read_reg(0x28c0) & 1; + unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1; struct yuv_playback_info *yi = &itv->yuv_info; int last_dma_frame = atomic_read(&yi->next_dma_frame); struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame]; @@ -778,6 +779,14 @@ static void ivtv_irq_vsync(struct ivtv *itv) } } if (frame != (itv->last_vsync_field & 1)) { + static const struct v4l2_event evtop = { + .type = V4L2_EVENT_VSYNC, + .u.vsync.field = V4L2_FIELD_TOP, + }; + static const struct v4l2_event evbottom = { + .type = V4L2_EVENT_VSYNC, + .u.vsync.field = V4L2_FIELD_BOTTOM, + }; struct ivtv_stream *s = ivtv_get_output_stream(itv); itv->last_vsync_field += 1; @@ -791,10 +800,12 @@ static void ivtv_irq_vsync(struct ivtv *itv) if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) { set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags); wake_up(&itv->event_waitq); + if (s) + wake_up(&s->waitq); } + if (s && s->vdev) + v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom); wake_up(&itv->vsync_waitq); - if (s) - wake_up(&s->waitq); /* Send VBI to saa7127 */ if (frame && (itv->output_mode == OUT_PASSTHROUGH || @@ -852,9 +863,11 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) */ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { /* vsync is enabled, see if we're in a new field */ - if ((itv->last_vsync_field & 1) != (read_reg(0x28c0) & 1)) { + if ((itv->last_vsync_field & 1) != + (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) { /* New field, looks like we missed it */ - IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16); + IVTV_DEBUG_YUV("VSync interrupt missed %d\n", + read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16); vsync_force = 1; } } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 1f9387f6ca2..de4288cc188 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 <media/v4l2-event.h> static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { .owner = THIS_MODULE, @@ -343,7 +344,10 @@ static void ivtv_vbi_setup(struct ivtv *itv) ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); /* setup VBI registers */ - v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in); + if (raw) + v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi); + else + v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced); /* determine number of lines and total number of VBI bytes. A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 @@ -581,10 +585,9 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); /* Avoid unpredictable PCI bus hang - disable video clocks */ v4l2_subdev_call(itv->sd_video, video, s_stream, 0); - ivtv_msleep_timeout(150, 1); + ivtv_msleep_timeout(300, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); v4l2_subdev_call(itv->sd_video, video, s_stream, 1); - ivtv_msleep_timeout(150, 1); } /* begin_capture */ @@ -830,6 +833,10 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); } + /* Raw-passthrough is implied on start. Make sure it's stopped so + the encoder will re-initialize when next started */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7); + wake_up(&s->waitq); return 0; @@ -837,6 +844,9 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) { + static const struct v4l2_event ev = { + .type = V4L2_EVENT_EOS, + }; struct ivtv *itv = s->itv; if (s->vdev == NULL) @@ -888,6 +898,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); wake_up(&itv->event_waitq); + v4l2_event_queue(s->vdev, &ev); /* wake up wait queues */ wake_up(&s->waitq); diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index f420d31b937..e1c347e5ebd 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -38,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled) data.data[9] = itv->vbi.vps_payload.data[2]; data.data[10] = itv->vbi.vps_payload.data[3]; data.data[11] = itv->vbi.vps_payload.data[4]; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); } static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) @@ -52,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) data.line = (mode & 1) ? 21 : 0; data.data[0] = cc->odd[0]; data.data[1] = cc->odd[1]; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); data.field = 1; data.line = (mode & 2) ? 21 : 0; data.data[0] = cc->even[0]; data.data[1] = cc->even[1]; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); } static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) @@ -80,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) data.line = enabled ? 23 : 0; data.data[0] = mode & 0xff; data.data[1] = (mode >> 8) & 0xff; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); } static int odd_parity(u8 c) @@ -134,7 +134,7 @@ void ivtv_write_vbi(struct ivtv *itv, const struct v4l2_sliced_vbi_data *sliced, } } } - if (found_cc && vi->cc_payload_idx < sizeof(vi->cc_payload)) { + if (found_cc && vi->cc_payload_idx < ARRAY_SIZE(vi->cc_payload)) { vi->cc_payload[vi->cc_payload_idx++] = cc; set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); } @@ -316,7 +316,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 continue; } vbi.p = p + 4; - v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi); + v4l2_subdev_call(itv->sd_video, vbi, decode_vbi_line, &vbi); if (vbi.type && !(lines & (1 << vbi.line))) { lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; @@ -440,7 +440,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_WSS_625; data.field = 0; - if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { ivtv_set_wss(itv, 1, data.data[0] & 0xf); vi->wss_missing_cnt = 0; } else if (vi->wss_missing_cnt == 4) { @@ -454,13 +454,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_CAPTION_525; data.field = 0; - if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { mode |= 1; cc.odd[0] = data.data[0]; cc.odd[1] = data.data[1]; } data.field = 1; - if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { mode |= 2; cc.even[0] = data.data[0]; cc.even[1] = data.data[1]; diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index de2ff1c6ac3..49e1a283ed3 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -460,7 +460,7 @@ static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long ar vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC; - trace = read_reg(0x028c0) >> 16; + trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16; if (itv->is_50hz && trace > 312) trace -= 312; else if (itv->is_60hz && trace > 262) diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c new file mode 100644 index 00000000000..554eaf14012 --- /dev/null +++ b/drivers/media/video/mem2mem_testdev.c @@ -0,0 +1,1052 @@ +/* + * A virtual v4l2-mem2mem example device. + * + * This is a virtual device driver for testing mem-to-mem videobuf framework. + * It simulates a device that uses memory buffers for both source and + * destination, processes the data and issues an "irq" (simulated by a timer). + * The device is capable of multi-instance, multi-buffer-per-transaction + * operation (via the mem2mem framework). + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <p.osciak@samsung.com> + * Marek Szyprowski, <m.szyprowski@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/delay.h> +#include <linux/fs.h> +#include <linux/version.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <linux/platform_device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf-vmalloc.h> + +#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev" + +MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); +MODULE_AUTHOR("Pawel Osciak, <p.osciak@samsung.com>"); +MODULE_LICENSE("GPL"); + + +#define MIN_W 32 +#define MIN_H 32 +#define MAX_W 640 +#define MAX_H 480 +#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */ + +/* Flags that indicate a format can be used for capture/output */ +#define MEM2MEM_CAPTURE (1 << 0) +#define MEM2MEM_OUTPUT (1 << 1) + +#define MEM2MEM_NAME "m2m-testdev" + +/* Per queue */ +#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME +/* In bytes, per queue */ +#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024) + +/* Default transaction time in msec */ +#define MEM2MEM_DEF_TRANSTIME 1000 +/* Default number of buffers per transaction */ +#define MEM2MEM_DEF_TRANSLEN 1 +#define MEM2MEM_COLOR_STEP (0xff >> 4) +#define MEM2MEM_NUM_TILES 8 + +#define dprintk(dev, fmt, arg...) \ + v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + + +void m2mtest_dev_release(struct device *dev) +{} + +static struct platform_device m2mtest_pdev = { + .name = MEM2MEM_NAME, + .dev.release = m2mtest_dev_release, +}; + +struct m2mtest_fmt { + char *name; + u32 fourcc; + int depth; + /* Types the format can be used for */ + u32 types; +}; + +static struct m2mtest_fmt formats[] = { + { + .name = "RGB565 (BE)", + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .depth = 16, + /* Both capture and output format */ + .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, + }, + { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + /* Output-only format */ + .types = MEM2MEM_OUTPUT, + }, +}; + +/* Per-queue, driver-specific private data */ +struct m2mtest_q_data { + unsigned int width; + unsigned int height; + unsigned int sizeimage; + struct m2mtest_fmt *fmt; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +/* Source and destination queue data */ +static struct m2mtest_q_data q_data[2]; + +static struct m2mtest_q_data *get_q_data(enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &q_data[V4L2_M2M_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &q_data[V4L2_M2M_DST]; + default: + BUG(); + } + return NULL; +} + +#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE +#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1) + +static struct v4l2_queryctrl m2mtest_ctrls[] = { + { + .id = V4L2_CID_TRANS_TIME_MSEC, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Transaction time (msec)", + .minimum = 1, + .maximum = 10000, + .step = 100, + .default_value = 1000, + .flags = 0, + }, { + .id = V4L2_CID_TRANS_NUM_BUFS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Buffers per transaction", + .minimum = 1, + .maximum = MEM2MEM_DEF_NUM_BUFS, + .step = 1, + .default_value = 1, + .flags = 0, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static struct m2mtest_fmt *find_format(struct v4l2_format *f) +{ + struct m2mtest_fmt *fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &formats[k]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + break; + } + + if (k == NUM_FORMATS) + return NULL; + + return &formats[k]; +} + +struct m2mtest_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd; + + atomic_t num_inst; + struct mutex dev_mutex; + spinlock_t irqlock; + + struct timer_list timer; + + struct v4l2_m2m_dev *m2m_dev; +}; + +struct m2mtest_ctx { + struct m2mtest_dev *dev; + + /* Processed buffers in this transaction */ + u8 num_processed; + + /* Transaction length (i.e. how many buffers per transaction) */ + u32 translen; + /* Transaction time (i.e. simulated processing time) in milliseconds */ + u32 transtime; + + /* Abort requested by m2m */ + int aborting; + + struct v4l2_m2m_ctx *m2m_ctx; +}; + +struct m2mtest_buffer { + /* vb must be first! */ + struct videobuf_buffer vb; +}; + +static struct v4l2_queryctrl *get_ctrl(int id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) { + if (id == m2mtest_ctrls[i].id) + return &m2mtest_ctrls[i]; + } + + return NULL; +} + +static int device_process(struct m2mtest_ctx *ctx, + struct m2mtest_buffer *in_buf, + struct m2mtest_buffer *out_buf) +{ + struct m2mtest_dev *dev = ctx->dev; + u8 *p_in, *p_out; + int x, y, t, w; + int tile_w, bytes_left; + struct videobuf_queue *src_q; + struct videobuf_queue *dst_q; + + src_q = v4l2_m2m_get_src_vq(ctx->m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(ctx->m2m_ctx); + p_in = videobuf_queue_to_vaddr(src_q, &in_buf->vb); + p_out = videobuf_queue_to_vaddr(dst_q, &out_buf->vb); + if (!p_in || !p_out) { + v4l2_err(&dev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return -EFAULT; + } + + if (in_buf->vb.size < out_buf->vb.size) { + v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); + return -EINVAL; + } + + tile_w = (in_buf->vb.width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) + / MEM2MEM_NUM_TILES; + bytes_left = in_buf->vb.bytesperline - tile_w * MEM2MEM_NUM_TILES; + w = 0; + + for (y = 0; y < in_buf->vb.height; ++y) { + for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { + if (w & 0x1) { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ + MEM2MEM_COLOR_STEP; + } else { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ - MEM2MEM_COLOR_STEP; + } + ++w; + } + p_in += bytes_left; + p_out += bytes_left; + } + + return 0; +} + +static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout) +{ + dprintk(dev, "Scheduling a simulated irq\n"); + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout)); +} + +/* + * mem2mem callbacks + */ + +/** + * job_ready() - check whether an instance is ready to be scheduled to run + */ +static int job_ready(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + + if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen + || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) { + dprintk(ctx->dev, "Not enough buffers available\n"); + return 0; + } + + return 1; +} + +static void job_abort(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +/* device_run() - prepares and starts the device + * + * This simulates all the immediate preparations required before starting + * a device. This will be called by the framework when it decides to schedule + * a particular instance. + */ +static void device_run(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + struct m2mtest_dev *dev = ctx->dev; + struct m2mtest_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + + device_process(ctx, src_buf, dst_buf); + + /* Run a timer, which simulates a hardware irq */ + schedule_irq(dev, ctx->transtime); +} + + +static void device_isr(unsigned long priv) +{ + struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv; + struct m2mtest_ctx *curr_ctx; + struct m2mtest_buffer *src_buf, *dst_buf; + unsigned long flags; + + curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev); + + if (NULL == curr_ctx) { + printk(KERN_ERR + "Instance released before the end of transaction\n"); + return; + } + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + curr_ctx->num_processed++; + + if (curr_ctx->num_processed == curr_ctx->translen + || curr_ctx->aborting) { + dprintk(curr_ctx->dev, "Finishing transaction\n"); + curr_ctx->num_processed = 0; + spin_lock_irqsave(&m2mtest_dev->irqlock, flags); + src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; + wake_up(&src_buf->vb.done); + wake_up(&dst_buf->vb.done); + spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); + v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx); + } else { + spin_lock_irqsave(&m2mtest_dev->irqlock, flags); + src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; + wake_up(&src_buf->vb.done); + wake_up(&dst_buf->vb.done); + spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); + device_run(curr_ctx); + } +} + + +/* + * video ioctls + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(0, 1, 0); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; + + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ + int i, num; + struct m2mtest_fmt *fmt; + + num = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (formats[i].types & type) { + /* index-th format of type type found ? */ + if (num == f->index) + break; + /* Correct type but haven't reached our index yet, + * just increment per-type index */ + ++num; + } + } + + if (i < NUM_FORMATS) { + /* Format found */ + fmt = &formats[i]; + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + return 0; + } + + /* Format not found */ + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f, MEM2MEM_CAPTURE); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f, MEM2MEM_OUTPUT); +} + +static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) +{ + struct videobuf_queue *vq; + struct m2mtest_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(f->type); + + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.field = vq->field; + f->fmt.pix.pixelformat = q_data->fmt->fourcc; + f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + f->fmt.pix.sizeimage = q_data->sizeimage; + + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(priv, f); +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(priv, f); +} + +static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt) +{ + enum v4l2_field field; + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != field) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported */ + f->fmt.pix.field = field; + + if (f->fmt.pix.height < MIN_H) + f->fmt.pix.height = MIN_H; + else if (f->fmt.pix.height > MAX_H) + f->fmt.pix.height = MAX_H; + + if (f->fmt.pix.width < MIN_W) + f->fmt.pix.width = MIN_W; + else if (f->fmt.pix.width > MAX_W) + f->fmt.pix.width = MAX_W; + + f->fmt.pix.width &= ~DIM_ALIGN_MASK; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct m2mtest_fmt *fmt; + struct m2mtest_ctx *ctx = priv; + + fmt = find_format(f); + if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct m2mtest_fmt *fmt; + struct m2mtest_ctx *ctx = priv; + + fmt = find_format(f); + if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) +{ + struct m2mtest_q_data *q_data; + struct videobuf_queue *vq; + int ret = 0; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(f->type); + if (!q_data) + return -EINVAL; + + mutex_lock(&vq->vb_lock); + + if (videobuf_queue_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + q_data->fmt = find_format(f); + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + q_data->sizeimage = q_data->width * q_data->height + * q_data->fmt->depth >> 3; + vq->field = f->fmt.pix.field; + + dprintk(ctx->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %d\n", + f->type, q_data->width, q_data->height, q_data->fmt->fourcc); + +out: + mutex_unlock(&vq->vb_lock); + return ret; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(priv, f); +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(priv, f); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +static int vidioc_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; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct m2mtest_ctx *ctx = priv; + + switch (ctrl->id) { + case V4L2_CID_TRANS_TIME_MSEC: + ctrl->value = ctx->transtime; + break; + + case V4L2_CID_TRANS_NUM_BUFS: + ctrl->value = ctx->translen; + break; + + default: + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + + return 0; +} + +static int check_ctrl_val(struct m2mtest_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) { + v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n"); + return -ERANGE; + } + + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct m2mtest_ctx *ctx = priv; + int ret = 0; + + ret = check_ctrl_val(ctx, ctrl); + if (ret != 0) + return ret; + + switch (ctrl->id) { + case V4L2_CID_TRANS_TIME_MSEC: + ctx->transtime = ctrl->value; + break; + + case V4L2_CID_TRANS_NUM_BUFS: + ctx->translen = ctrl->value; + break; + + default: + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + + return 0; +} + + +static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + + +/* + * Queue operations + */ + +static void m2mtest_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + + dprintk(ctx->dev, "type: %d, index: %d, state: %d\n", + vq->type, vb->i, vb->state); + + videobuf_vmalloc_free(vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int m2mtest_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + struct m2mtest_q_data *q_data; + + q_data = get_q_data(vq->type); + + *size = q_data->width * q_data->height * q_data->fmt->depth >> 3; + dprintk(ctx->dev, "size:%d, w/h %d/%d, depth: %d\n", + *size, q_data->width, q_data->height, q_data->fmt->depth); + + if (0 == *count) + *count = MEM2MEM_DEF_NUM_BUFS; + + while (*size * *count > MEM2MEM_VID_MEM_LIMIT) + (*count)--; + + v4l2_info(&ctx->dev->v4l2_dev, + "%d buffers of size %d set up.\n", *count, *size); + + return 0; +} + +static int m2mtest_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + struct m2mtest_q_data *q_data; + int ret; + + dprintk(ctx->dev, "type: %d, index: %d, state: %d\n", + vq->type, vb->i, vb->state); + + q_data = get_q_data(vq->type); + + if (vb->baddr) { + /* User-provided buffer */ + if (vb->bsize < q_data->sizeimage) { + /* Buffer too small to fit a frame */ + v4l2_err(&ctx->dev->v4l2_dev, + "User-provided buffer too small\n"); + return -EINVAL; + } + } else if (vb->state != VIDEOBUF_NEEDS_INIT + && vb->bsize < q_data->sizeimage) { + /* We provide the buffer, but it's already been initialized + * and is too small */ + return -EINVAL; + } + + vb->width = q_data->width; + vb->height = q_data->height; + vb->bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + vb->size = q_data->sizeimage; + vb->field = field; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) { + v4l2_err(&ctx->dev->v4l2_dev, + "Iolock failed\n"); + goto fail; + } + } + + vb->state = VIDEOBUF_PREPARED; + + return 0; +fail: + m2mtest_buf_release(vq, vb); + return ret; +} + +static void m2mtest_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + + v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); +} + +static struct videobuf_queue_ops m2mtest_qops = { + .buf_setup = m2mtest_buf_setup, + .buf_prepare = m2mtest_buf_prepare, + .buf_queue = m2mtest_buf_queue, + .buf_release = m2mtest_buf_release, +}; + +static void queue_init(void *priv, struct videobuf_queue *vq, + enum v4l2_buf_type type) +{ + struct m2mtest_ctx *ctx = priv; + + videobuf_queue_vmalloc_init(vq, &m2mtest_qops, ctx->dev->v4l2_dev.dev, + &ctx->dev->irqlock, type, V4L2_FIELD_NONE, + sizeof(struct m2mtest_buffer), priv); +} + + +/* + * File operations + */ +static int m2mtest_open(struct file *file) +{ + struct m2mtest_dev *dev = video_drvdata(file); + struct m2mtest_ctx *ctx = NULL; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + file->private_data = ctx; + ctx->dev = dev; + ctx->translen = MEM2MEM_DEF_TRANSLEN; + ctx->transtime = MEM2MEM_DEF_TRANSTIME; + ctx->num_processed = 0; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, dev->m2m_dev, queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + int ret = PTR_ERR(ctx->m2m_ctx); + + kfree(ctx); + return ret; + } + + atomic_inc(&dev->num_inst); + + dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); + + return 0; +} + +static int m2mtest_release(struct file *file) +{ + struct m2mtest_dev *dev = video_drvdata(file); + struct m2mtest_ctx *ctx = file->private_data; + + dprintk(dev, "Releasing instance %p\n", ctx); + + v4l2_m2m_ctx_release(ctx->m2m_ctx); + kfree(ctx); + + atomic_dec(&dev->num_inst); + + return 0; +} + +static unsigned int m2mtest_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct m2mtest_ctx *ctx = (struct m2mtest_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; + + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations m2mtest_fops = { + .owner = THIS_MODULE, + .open = m2mtest_open, + .release = m2mtest_release, + .poll = m2mtest_poll, + .ioctl = video_ioctl2, + .mmap = m2mtest_mmap, +}; + +static struct video_device m2mtest_videodev = { + .name = MEM2MEM_NAME, + .fops = &m2mtest_fops, + .ioctl_ops = &m2mtest_ioctl_ops, + .minor = -1, + .release = video_device_release, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_ready = job_ready, + .job_abort = job_abort, +}; + +static int m2mtest_probe(struct platform_device *pdev) +{ + struct m2mtest_dev *dev; + struct video_device *vfd; + int ret; + + dev = kzalloc(sizeof *dev, GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->irqlock); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + goto free_dev; + + atomic_set(&dev->num_inst, 0); + mutex_init(&dev->dev_mutex); + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto unreg_dev; + } + + *vfd = m2mtest_videodev; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto rel_vdev; + } + + video_set_drvdata(vfd, dev); + snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name); + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME + "Device registered as /dev/video%d\n", vfd->num); + + setup_timer(&dev->timer, device_isr, (long)dev); + platform_set_drvdata(pdev, dev); + + dev->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(dev->m2m_dev); + goto err_m2m; + } + + return 0; + +err_m2m: + video_unregister_device(dev->vfd); +rel_vdev: + video_device_release(vfd); +unreg_dev: + v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); + + return ret; +} + +static int m2mtest_remove(struct platform_device *pdev) +{ + struct m2mtest_dev *dev = + (struct m2mtest_dev *)platform_get_drvdata(pdev); + + v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME); + v4l2_m2m_release(dev->m2m_dev); + del_timer_sync(&dev->timer); + video_unregister_device(dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + + return 0; +} + +static struct platform_driver m2mtest_pdrv = { + .probe = m2mtest_probe, + .remove = m2mtest_remove, + .driver = { + .name = MEM2MEM_NAME, + .owner = THIS_MODULE, + }, +}; + +static void __exit m2mtest_exit(void) +{ + platform_driver_unregister(&m2mtest_pdrv); + platform_device_unregister(&m2mtest_pdev); +} + +static int __init m2mtest_init(void) +{ + int ret; + + ret = platform_device_register(&m2mtest_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&m2mtest_pdrv); + if (ret) + platform_device_unregister(&m2mtest_pdev); + + return 0; +} + +module_init(m2mtest_init); +module_exit(m2mtest_exit); + diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 4404e5ef818..2be23bccd3c 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -30,9 +30,10 @@ #include <linux/pci.h> #include <linux/sched.h> #include <linux/init.h> -#include <linux/videodev.h> #include <linux/gfp.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -1168,22 +1169,22 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) case V4L2_CID_BRIGHTNESS: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); - meye.picture.brightness = c->value << 10; + meye.brightness = c->value << 10; break; case V4L2_CID_HUE: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERAHUE, c->value); - meye.picture.hue = c->value << 10; + meye.hue = c->value << 10; break; case V4L2_CID_CONTRAST: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); - meye.picture.contrast = c->value << 10; + meye.contrast = c->value << 10; break; case V4L2_CID_SATURATION: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); - meye.picture.colour = c->value << 10; + meye.colour = c->value << 10; break; case V4L2_CID_AGC: sony_pic_camera_command( @@ -1221,16 +1222,16 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) mutex_lock(&meye.lock); switch (c->id) { case V4L2_CID_BRIGHTNESS: - c->value = meye.picture.brightness >> 10; + c->value = meye.brightness >> 10; break; case V4L2_CID_HUE: - c->value = meye.picture.hue >> 10; + c->value = meye.hue >> 10; break; case V4L2_CID_CONTRAST: - c->value = meye.picture.contrast >> 10; + c->value = meye.contrast >> 10; break; case V4L2_CID_SATURATION: - c->value = meye.picture.colour >> 10; + c->value = meye.colour >> 10; break; case V4L2_CID_AGC: c->value = meye.params.agc; @@ -1729,6 +1730,7 @@ static int meye_resume(struct pci_dev *pdev) static int __devinit meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { + struct v4l2_device *v4l2_dev = &meye.v4l2_dev; int ret = -EBUSY; unsigned long mchip_adr; @@ -1737,70 +1739,75 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outnotdev; } + ret = v4l2_device_register(&pcidev->dev, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return ret; + } ret = -ENOMEM; meye.mchip_dev = pcidev; - meye.video_dev = video_device_alloc(); - if (!meye.video_dev) { - printk(KERN_ERR "meye: video_device_alloc() failed!\n"); + meye.vdev = video_device_alloc(); + if (!meye.vdev) { + v4l2_err(v4l2_dev, "video_device_alloc() failed!\n"); goto outnotdev; } meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE); if (!meye.grab_temp) { - printk(KERN_ERR "meye: grab buffer allocation failed\n"); + v4l2_err(v4l2_dev, "grab buffer allocation failed\n"); goto outvmalloc; } spin_lock_init(&meye.grabq_lock); if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL)) { - printk(KERN_ERR "meye: fifo allocation failed\n"); + v4l2_err(v4l2_dev, "fifo allocation failed\n"); goto outkfifoalloc1; } spin_lock_init(&meye.doneq_lock); if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL)) { - printk(KERN_ERR "meye: fifo allocation failed\n"); + v4l2_err(v4l2_dev, "fifo allocation failed\n"); goto outkfifoalloc2; } - memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); - meye.video_dev->parent = &meye.mchip_dev->dev; + memcpy(meye.vdev, &meye_template, sizeof(meye_template)); + meye.vdev->v4l2_dev = &meye.v4l2_dev; ret = -EIO; if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { - printk(KERN_ERR "meye: unable to power on the camera\n"); - printk(KERN_ERR "meye: did you enable the camera in " + v4l2_err(v4l2_dev, "meye: unable to power on the camera\n"); + v4l2_err(v4l2_dev, "meye: did you enable the camera in " "sonypi using the module options ?\n"); goto outsonypienable; } if ((ret = pci_enable_device(meye.mchip_dev))) { - printk(KERN_ERR "meye: pci_enable_device failed\n"); + v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n"); goto outenabledev; } mchip_adr = pci_resource_start(meye.mchip_dev,0); if (!mchip_adr) { - printk(KERN_ERR "meye: mchip has no device base address\n"); + v4l2_err(v4l2_dev, "meye: mchip has no device base address\n"); goto outregions; } if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), pci_resource_len(meye.mchip_dev, 0), "meye")) { - printk(KERN_ERR "meye: request_mem_region failed\n"); + v4l2_err(v4l2_dev, "meye: request_mem_region failed\n"); goto outregions; } meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); if (!meye.mchip_mmregs) { - printk(KERN_ERR "meye: ioremap failed\n"); + v4l2_err(v4l2_dev, "meye: ioremap failed\n"); goto outremap; } meye.mchip_irq = pcidev->irq; if (request_irq(meye.mchip_irq, meye_irq, IRQF_DISABLED | IRQF_SHARED, "meye", meye_irq)) { - printk(KERN_ERR "meye: request_irq failed\n"); + v4l2_err(v4l2_dev, "request_irq failed\n"); goto outreqirq; } @@ -1824,21 +1831,18 @@ static int __devinit meye_probe(struct pci_dev *pcidev, msleep(1); mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); - if (video_register_device(meye.video_dev, VFL_TYPE_GRABBER, + if (video_register_device(meye.vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - printk(KERN_ERR "meye: video_register_device failed\n"); + v4l2_err(v4l2_dev, "video_register_device failed\n"); goto outvideoreg; } mutex_init(&meye.lock); init_waitqueue_head(&meye.proc_list); - meye.picture.depth = 16; - meye.picture.palette = VIDEO_PALETTE_YUV422; - meye.picture.brightness = 32 << 10; - meye.picture.hue = 32 << 10; - meye.picture.colour = 32 << 10; - meye.picture.contrast = 32 << 10; - meye.picture.whiteness = 0; + meye.brightness = 32 << 10; + meye.hue = 32 << 10; + meye.colour = 32 << 10; + meye.contrast = 32 << 10; meye.params.subsample = 0; meye.params.quality = 8; meye.params.sharpness = 32; @@ -1854,9 +1858,9 @@ static int __devinit meye_probe(struct pci_dev *pcidev, sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); - printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n", + v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n", MEYE_DRIVER_VERSION); - printk(KERN_INFO "meye: mchip KL5A72002 rev. %d, base %lx, irq %d\n", + v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n", meye.mchip_dev->revision, mchip_adr, meye.mchip_irq); return 0; @@ -1879,14 +1883,14 @@ outkfifoalloc2: outkfifoalloc1: vfree(meye.grab_temp); outvmalloc: - video_device_release(meye.video_dev); + video_device_release(meye.vdev); outnotdev: return ret; } static void __devexit meye_remove(struct pci_dev *pcidev) { - video_unregister_device(meye.video_dev); + video_unregister_device(meye.vdev); mchip_hic_stop(); diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index 1321ad5d659..4bdeb03f164 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -31,7 +31,7 @@ #define _MEYE_PRIV_H_ #define MEYE_DRIVER_MAJORVERSION 1 -#define MEYE_DRIVER_MINORVERSION 13 +#define MEYE_DRIVER_MINORVERSION 14 #define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ __stringify(MEYE_DRIVER_MINORVERSION) @@ -289,6 +289,7 @@ struct meye_grab_buffer { /* Motion Eye device structure */ struct meye { + struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ struct pci_dev *mchip_dev; /* pci device */ u8 mchip_irq; /* irq */ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ @@ -308,8 +309,11 @@ struct meye { struct kfifo doneq; /* queue for grabbed buffers */ spinlock_t doneq_lock; /* lock protecting the queue */ wait_queue_head_t proc_list; /* wait queue */ - struct video_device *video_dev; /* video device parameters */ - struct video_picture picture; /* video picture parameters */ + struct video_device *vdev; /* video device parameters */ + u16 brightness; + u16 hue; + u16 contrast; + u16 colour; struct meye_params params; /* additional parameters */ unsigned long in_use; /* set to 1 if the device is in use */ #ifdef CONFIG_PM diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index a9061bff79b..78b4e091d2d 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -8,14 +8,16 @@ * published by the Free Software Foundation. */ -#include <linux/videodev2.h> -#include <linux/slab.h> +#include <linux/device.h> #include <linux/i2c.h> #include <linux/log2.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/videodev2.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-subdev.h> /* * mt9t031 i2c address 0x5d @@ -681,12 +683,66 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } /* + * Power Management: + * This function does nothing for now but must be present for pm to work + */ +static int mt9t031_runtime_suspend(struct device *dev) +{ + return 0; +} + +/* + * Power Management: + * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged + * they are however changed at reset if the platform hook is present + * thus we rewrite them with the values stored by the driver + */ +static int mt9t031_runtime_resume(struct device *dev) +{ + struct video_device *vdev = to_video_device(dev); + struct soc_camera_device *icd = container_of(vdev->parent, + struct soc_camera_device, dev); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + + int ret; + u16 xbin, ybin; + + xbin = min(mt9t031->xskip, (u16)3); + ybin = min(mt9t031->yskip, (u16)3); + + ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (mt9t031->xskip - 1)); + if (ret < 0) + return ret; + + ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (mt9t031->yskip - 1)); + if (ret < 0) + return ret; + + return 0; +} + +static struct dev_pm_ops mt9t031_dev_pm_ops = { + .runtime_suspend = mt9t031_runtime_suspend, + .runtime_resume = mt9t031_runtime_resume, +}; + +static struct device_type mt9t031_dev_type = { + .name = "MT9T031", + .pm = &mt9t031_dev_pm_ops, +}; + +/* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ static int mt9t031_video_probe(struct i2c_client *client) { struct mt9t031 *mt9t031 = to_mt9t031(client); + struct video_device *vdev = soc_camera_i2c_to_vdev(client); s32 data; int ret; @@ -712,6 +768,8 @@ static int mt9t031_video_probe(struct i2c_client *client) ret = mt9t031_idle(client); if (ret < 0) dev_err(&client->dev, "Failed to initialise the camera\n"); + else + vdev->dev.type = &mt9t031_dev_type; /* mt9t031_idle() has reset the chip to default. */ mt9t031->exposure = 255; diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 1a34d2993e9..e5bae4c9393 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -325,7 +325,7 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) if (ret < 0) return ret; - dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height); + dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); mt9v022->rect = rect; diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 34a66019190..5c17f9ec3d7 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -139,8 +139,8 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (!*count) *count = 32; - while (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - (*count)--; + if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index bd297f567dc..d477e305800 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -796,7 +796,7 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) * FIXME: learn to use stride != width, then we can keep stride properly aligned * and support arbitrary (even) widths. */ -static inline void stride_align(__s32 *width) +static inline void stride_align(__u32 *width) { if (((*width + 7) & ~7) < 4096) *width = (*width + 7) & ~7; @@ -844,7 +844,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, * So far only direct camera-to-memory is supported */ if (channel_change_requested(icd, rect)) { - int ret = acquire_dma_channel(mx3_cam); + ret = acquire_dma_channel(mx3_cam); if (ret < 0) return ret; } diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig new file mode 100644 index 00000000000..97c53949ca8 --- /dev/null +++ b/drivers/media/video/omap/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_OMAP2_VOUT + tristate "OMAP2/OMAP3 V4L2-Display driver" + depends on ARCH_OMAP24XX || ARCH_OMAP34XX + select VIDEOBUF_GEN + select VIDEOBUF_DMA_SG + select OMAP2_DSS + select OMAP2_VRAM + select OMAP2_VRFB + default n + ---help--- + V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile new file mode 100644 index 00000000000..b8bab00ad01 --- /dev/null +++ b/drivers/media/video/omap/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the omap video device drivers. +# + +# OMAP2/3 Display driver +omap-vout-mod-objs := omap_vout.o omap_voutlib.o +obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout-mod.o diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c new file mode 100644 index 00000000000..4c0ab499228 --- /dev/null +++ b/drivers/media/video/omap/omap_vout.c @@ -0,0 +1,2643 @@ +/* + * omap_vout.c + * + * Copyright (C) 2005-2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Leveraged code from the OMAP2 camera driver + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP24xx camera controller. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2010 Texas Instruments. + * + * History: + * 20-APR-2006 Khasim Modified VRFB based Rotation, + * The image data is always read from 0 degree + * view and written + * to the virtual space of desired rotation angle + * 4-DEC-2006 Jian Changed to support better memory management + * + * 17-Nov-2008 Hardik Changed driver to use video_ioctl2 + * + * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/irq.h> +#include <linux/videodev2.h> + +#include <media/videobuf-dma-sg.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include <plat/dma.h> +#include <plat/vram.h> +#include <plat/vrfb.h> +#include <plat/display.h> + +#include "omap_voutlib.h" +#include "omap_voutdef.h" + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP Video for Linux Video out driver"); +MODULE_LICENSE("GPL"); + + +/* Driver Configuration macros */ +#define VOUT_NAME "omap_vout" + +enum omap_vout_channels { + OMAP_VIDEO1, + OMAP_VIDEO2, +}; + +enum dma_channel_state { + DMA_CHAN_NOT_ALLOTED, + DMA_CHAN_ALLOTED, +}; + +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 + +/* Max Resolution supported by the driver */ +#define VID_MAX_WIDTH 1280 /* Largest width */ +#define VID_MAX_HEIGHT 720 /* Largest height */ + +/* Mimimum requirement is 2x2 for DSS */ +#define VID_MIN_WIDTH 2 +#define VID_MIN_HEIGHT 2 + +/* 2048 x 2048 is max res supported by OMAP display controller */ +#define MAX_PIXELS_PER_LINE 2048 + +#define VRFB_TX_TIMEOUT 1000 +#define VRFB_NUM_BUFS 4 + +/* Max buffer size tobe allocated during init */ +#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4) + +static struct videobuf_queue_ops video_vbq_ops; +/* Variables configurable through module params*/ +static u32 video1_numbuffers = 3; +static u32 video2_numbuffers = 3; +static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +static u32 vid1_static_vrfb_alloc; +static u32 vid2_static_vrfb_alloc; +static int debug; + +/* Module parameters */ +module_param(video1_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video1_numbuffers, + "Number of buffers to be allocated at init time for Video1 device."); + +module_param(video2_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video2_numbuffers, + "Number of buffers to be allocated at init time for Video2 device."); + +module_param(video1_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video1_bufsize, + "Size of the buffer to be allocated for video1 device"); + +module_param(video2_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video2_bufsize, + "Size of the buffer to be allocated for video2 device"); + +module_param(vid1_static_vrfb_alloc, bool, S_IRUGO); +MODULE_PARM_DESC(vid1_static_vrfb_alloc, + "Static allocation of the VRFB buffer for video1 device"); + +module_param(vid2_static_vrfb_alloc, bool, S_IRUGO); +MODULE_PARM_DESC(vid2_static_vrfb_alloc, + "Static allocation of the VRFB buffer for video2 device"); + +module_param(debug, bool, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* list of image formats supported by OMAP2 video pipelines */ +const static struct v4l2_fmtdesc omap_formats[] = { + { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use + * this for RGB24 unpack mode, the last 8 bits are ignored + * */ + .description = "RGB32, le", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, + { + /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use + * this for RGB24 packed mode + * + */ + .description = "RGB24, le", + .pixelformat = V4L2_PIX_FMT_RGB24, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats)) + +/* + * Allocate buffers + */ +static unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr) +{ + u32 order, size; + unsigned long virt_addr, addr; + + size = PAGE_ALIGN(buf_size); + order = get_order(size); + virt_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, order); + addr = virt_addr; + + if (virt_addr) { + while (size > 0) { + SetPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + *phys_addr = (u32) virt_to_phys((void *) virt_addr); + return virt_addr; +} + +/* + * Free buffers + */ +static void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size) +{ + u32 order, size; + unsigned long addr = virtaddr; + + size = PAGE_ALIGN(buf_size); + order = get_order(size); + + while (size > 0) { + ClearPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + free_pages((unsigned long) virtaddr, order); +} + +/* + * Function for allocating video buffers + */ +static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, + unsigned int *count, int startindex) +{ + int i, j; + + for (i = 0; i < *count; i++) { + if (!vout->smsshado_virt_addr[i]) { + vout->smsshado_virt_addr[i] = + omap_vout_alloc_buffer(vout->smsshado_size, + &vout->smsshado_phy_addr[i]); + } + if (!vout->smsshado_virt_addr[i] && startindex != -1) { + if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex) + break; + } + if (!vout->smsshado_virt_addr[i]) { + for (j = 0; j < i; j++) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } + *count = 0; + return -ENOMEM; + } + memset((void *) vout->smsshado_virt_addr[i], 0, + vout->smsshado_size); + } + return 0; +} + +/* + * Try format + */ +static int omap_vout_try_format(struct v4l2_pix_format *pix) +{ + int ifmt, bpp = 0; + + pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT, + (u32)VID_MAX_HEIGHT); + pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH); + + for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) { + if (pix->pixelformat == omap_formats[ifmt].pixelformat) + break; + } + + if (ifmt == NUM_OUTPUT_FORMATS) + ifmt = 0; + + pix->pixelformat = omap_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_ANY; + pix->priv = 0; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + bpp = YUYV_BPP; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB565_BPP; + break; + case V4L2_PIX_FMT_RGB24: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB24_BPP; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB32_BPP; + break; + } + pix->bytesperline = pix->width * bpp; + pix->sizeimage = pix->bytesperline * pix->height; + + return bpp; +} + +/* + * omap_vout_uservirt_to_phys: This inline function is used to convert user + * space virtual address to physical address. + */ +static u32 omap_vout_uservirt_to_phys(u32 virtp) +{ + unsigned long physp = 0; + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + + vma = find_vma(mm, virtp); + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) { + physp = virt_to_phys((void *) virtp); + } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { + /* this will catch, kernel-allocated, mmaped-to-usermode + addresses */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + } else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, virtp, nr_pages, 1, + 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) { + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + } else { + printk(KERN_WARNING VOUT_NAME + "get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* + * Wakes up the application once the DMA transfer to VRFB space is completed. + */ +static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) +{ + struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; + + t->tx_status = 1; + wake_up_interruptible(&t->wait); +} + +/* + * Release the VRFB context once the module exits + */ +static void omap_vout_release_vrfb(struct omap_vout_device *vout) +{ + int i; + + for (i = 0; i < VRFB_NUM_BUFS; i++) + omap_vrfb_release_ctx(&vout->vrfb_context[i]); + + if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + omap_free_dma(vout->vrfb_dma_tx.dma_ch); + } +} + +/* + * Return true if rotation is 90 or 270 + */ +static inline int rotate_90_or_270(const struct omap_vout_device *vout) +{ + return (vout->rotation == dss_rotation_90_degree || + vout->rotation == dss_rotation_270_degree); +} + +/* + * Return true if rotation is enabled + */ +static inline int rotation_enabled(const struct omap_vout_device *vout) +{ + return vout->rotation || vout->mirror; +} + +/* + * Reverse the rotation degree if mirroring is enabled + */ +static inline int calc_rotation(const struct omap_vout_device *vout) +{ + if (!vout->mirror) + return vout->rotation; + + switch (vout->rotation) { + case dss_rotation_90_degree: + return dss_rotation_270_degree; + case dss_rotation_270_degree: + return dss_rotation_90_degree; + case dss_rotation_180_degree: + return dss_rotation_0_degree; + default: + return dss_rotation_180_degree; + } +} + +/* + * Free the V4L2 buffers + */ +static void omap_vout_free_buffers(struct omap_vout_device *vout) +{ + int i, numbuffers; + + /* Allocate memory for the buffers */ + numbuffers = (vout->vid) ? video2_numbuffers : video1_numbuffers; + vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize; + + for (i = 0; i < numbuffers; i++) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buffer_size); + vout->buf_phy_addr[i] = 0; + vout->buf_virt_addr[i] = 0; + } +} + +/* + * Free VRFB buffers + */ +static void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) +{ + int j; + + for (j = 0; j < VRFB_NUM_BUFS; j++) { + omap_vout_free_buffer(vout->smsshado_virt_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } +} + +/* + * Allocate the buffers for the VRFB space. Data is copied from V4L2 + * buffers to the VRFB buffers using the DMA engine. + */ +static int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, + unsigned int *count, unsigned int startindex) +{ + int i; + bool yuv_mode; + + /* Allocate the VRFB buffers only if the buffers are not + * allocated during init time. + */ + if ((rotation_enabled(vout)) && !vout->vrfb_static_allocation) + if (omap_vout_allocate_vrfb_buffers(vout, count, startindex)) + return -ENOMEM; + + if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 || + vout->dss_mode == OMAP_DSS_COLOR_UYVY) + yuv_mode = true; + else + yuv_mode = false; + + for (i = 0; i < *count; i++) + omap_vrfb_setup(&vout->vrfb_context[i], + vout->smsshado_phy_addr[i], vout->pix.width, + vout->pix.height, vout->bpp, yuv_mode); + + return 0; +} + +/* + * Convert V4L2 rotation to DSS rotation + * V4L2 understand 0, 90, 180, 270. + * Convert to 0, 1, 2 and 3 repsectively for DSS + */ +static int v4l2_rot_to_dss_rot(int v4l2_rotation, + enum dss_rotation *rotation, bool mirror) +{ + int ret = 0; + + switch (v4l2_rotation) { + case 90: + *rotation = dss_rotation_90_degree; + break; + case 180: + *rotation = dss_rotation_180_degree; + break; + case 270: + *rotation = dss_rotation_270_degree; + break; + case 0: + *rotation = dss_rotation_0_degree; + break; + default: + ret = -EINVAL; + } + return ret; +} + +/* + * Calculate the buffer offsets from which the streaming should + * start. This offset calculation is mainly required because of + * the VRFB 32 pixels alignment with rotation. + */ +static int omap_vout_calculate_offset(struct omap_vout_device *vout) +{ + struct omap_overlay *ovl; + enum dss_rotation rotation; + struct omapvideo_info *ovid; + bool mirroring = vout->mirror; + struct omap_dss_device *cur_display; + struct v4l2_rect *crop = &vout->crop; + struct v4l2_pix_format *pix = &vout->pix; + int *cropped_offset = &vout->cropped_offset; + int vr_ps = 1, ps = 2, temp_ps = 2; + int offset = 0, ctop = 0, cleft = 0, line_length = 0; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) + return -1; + + cur_display = ovl->manager->device; + rotation = calc_rotation(vout); + + if (V4L2_PIX_FMT_YUYV == pix->pixelformat || + V4L2_PIX_FMT_UYVY == pix->pixelformat) { + if (rotation_enabled(vout)) { + /* + * ps - Actual pixel size for YUYV/UYVY for + * VRFB/Mirroring is 4 bytes + * vr_ps - Virtually pixel size for YUYV/UYVY is + * 2 bytes + */ + ps = 4; + vr_ps = 2; + } else { + ps = 2; /* otherwise the pixel size is 2 byte */ + } + } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) { + ps = 4; + } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) { + ps = 3; + } + vout->ps = ps; + vout->vr_ps = vr_ps; + + if (rotation_enabled(vout)) { + line_length = MAX_PIXELS_PER_LINE; + ctop = (pix->height - crop->height) - crop->top; + cleft = (pix->width - crop->width) - crop->left; + } else { + line_length = pix->width; + } + vout->line_length = line_length; + switch (rotation) { + case dss_rotation_90_degree: + offset = vout->vrfb_context[0].yoffset * + vout->vrfb_context[0].bytespp; + temp_ps = ps / vr_ps; + if (mirroring == 0) { + *cropped_offset = offset + line_length * + temp_ps * cleft + crop->top * temp_ps; + } else { + *cropped_offset = offset + line_length * temp_ps * + cleft + crop->top * temp_ps + (line_length * + ((crop->width / (vr_ps)) - 1) * ps); + } + break; + case dss_rotation_180_degree: + offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset * + vout->vrfb_context[0].bytespp) + + (vout->vrfb_context[0].xoffset * + vout->vrfb_context[0].bytespp)); + if (mirroring == 0) { + *cropped_offset = offset + (line_length * ps * ctop) + + (cleft / vr_ps) * ps; + + } else { + *cropped_offset = offset + (line_length * ps * ctop) + + (cleft / vr_ps) * ps + (line_length * + (crop->height - 1) * ps); + } + break; + case dss_rotation_270_degree: + offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset * + vout->vrfb_context[0].bytespp; + temp_ps = ps / vr_ps; + if (mirroring == 0) { + *cropped_offset = offset + line_length * + temp_ps * crop->left + ctop * ps; + } else { + *cropped_offset = offset + line_length * + temp_ps * crop->left + ctop * ps + + (line_length * ((crop->width / vr_ps) - 1) * + ps); + } + break; + case dss_rotation_0_degree: + if (mirroring == 0) { + *cropped_offset = (line_length * ps) * + crop->top + (crop->left / vr_ps) * ps; + } else { + *cropped_offset = (line_length * ps) * + crop->top + (crop->left / vr_ps) * ps + + (line_length * (crop->height - 1) * ps); + } + break; + default: + *cropped_offset = (line_length * ps * crop->top) / + vr_ps + (crop->left * ps) / vr_ps + + ((crop->width / vr_ps) - 1) * ps; + break; + } + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n", + __func__, *cropped_offset); + return 0; +} + +/* + * Convert V4L2 pixel format to DSS pixel format + */ +static int video_mode_to_dss_mode(struct omap_vout_device *vout) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct v4l2_pix_format *pix = &vout->pix; + enum omap_color_mode mode; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + switch (pix->pixelformat) { + case 0: + break; + case V4L2_PIX_FMT_YUYV: + mode = OMAP_DSS_COLOR_YUV2; + break; + case V4L2_PIX_FMT_UYVY: + mode = OMAP_DSS_COLOR_UYVY; + break; + case V4L2_PIX_FMT_RGB565: + mode = OMAP_DSS_COLOR_RGB16; + break; + case V4L2_PIX_FMT_RGB24: + mode = OMAP_DSS_COLOR_RGB24P; + break; + case V4L2_PIX_FMT_RGB32: + mode = (ovl->id == OMAP_DSS_VIDEO1) ? + OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32; + break; + case V4L2_PIX_FMT_BGR32: + mode = OMAP_DSS_COLOR_RGBX32; + break; + default: + mode = -EINVAL; + } + return mode; +} + +/* + * Setup the overlay + */ +int omapvid_setup_overlay(struct omap_vout_device *vout, + struct omap_overlay *ovl, int posx, int posy, int outw, + int outh, u32 addr) +{ + int ret = 0; + struct omap_overlay_info info; + int cropheight, cropwidth, pixheight, pixwidth; + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && + (outw != vout->pix.width || outh != vout->pix.height)) { + ret = -EINVAL; + goto setup_ovl_err; + } + + vout->dss_mode = video_mode_to_dss_mode(vout); + if (vout->dss_mode == -EINVAL) { + ret = -EINVAL; + goto setup_ovl_err; + } + + /* Setup the input plane parameters according to + * rotation value selected. + */ + if (rotate_90_or_270(vout)) { + cropheight = vout->crop.width; + cropwidth = vout->crop.height; + pixheight = vout->pix.width; + pixwidth = vout->pix.height; + } else { + cropheight = vout->crop.height; + cropwidth = vout->crop.width; + pixheight = vout->pix.height; + pixwidth = vout->pix.width; + } + + ovl->get_overlay_info(ovl, &info); + info.paddr = addr; + info.vaddr = NULL; + info.width = cropwidth; + info.height = cropheight; + info.color_mode = vout->dss_mode; + info.mirror = vout->mirror; + info.pos_x = posx; + info.pos_y = posy; + info.out_width = outw; + info.out_height = outh; + info.global_alpha = vout->win.global_alpha; + if (!rotation_enabled(vout)) { + info.rotation = 0; + info.rotation_type = OMAP_DSS_ROT_DMA; + info.screen_width = pixwidth; + } else { + info.rotation = vout->rotation; + info.rotation_type = OMAP_DSS_ROT_VRFB; + info.screen_width = 2048; + } + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n" + "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n" + "out_height=%d rotation_type=%d screen_width=%d\n", + __func__, info.enabled, info.paddr, info.width, info.height, + info.color_mode, info.rotation, info.mirror, info.pos_x, + info.pos_y, info.out_width, info.out_height, info.rotation_type, + info.screen_width); + + ret = ovl->set_overlay_info(ovl, &info); + if (ret) + goto setup_ovl_err; + + return 0; + +setup_ovl_err: + v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n"); + return ret; +} + +/* + * Initialize the overlay structure + */ +int omapvid_init(struct omap_vout_device *vout, u32 addr) +{ + int ret = 0, i; + struct v4l2_window *win; + struct omap_overlay *ovl; + int posx, posy, outw, outh, temp; + struct omap_video_timings *timing; + struct omapvideo_info *ovid = &vout->vid_info; + + win = &vout->win; + for (i = 0; i < ovid->num_overlays; i++) { + ovl = ovid->overlays[i]; + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + + timing = &ovl->manager->device->panel.timings; + + outw = win->w.width; + outh = win->w.height; + switch (vout->rotation) { + case dss_rotation_90_degree: + /* Invert the height and width for 90 + * and 270 degree rotation + */ + temp = outw; + outw = outh; + outh = temp; + posy = (timing->y_res - win->w.width) - win->w.left; + posx = win->w.top; + break; + + case dss_rotation_180_degree: + posx = (timing->x_res - win->w.width) - win->w.left; + posy = (timing->y_res - win->w.height) - win->w.top; + break; + + case dss_rotation_270_degree: + temp = outw; + outw = outh; + outh = temp; + posy = win->w.left; + posx = (timing->x_res - win->w.height) - win->w.top; + break; + + default: + posx = win->w.left; + posy = win->w.top; + break; + } + + ret = omapvid_setup_overlay(vout, ovl, posx, posy, + outw, outh, addr); + if (ret) + goto omapvid_init_err; + } + return 0; + +omapvid_init_err: + v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n"); + return ret; +} + +/* + * Apply the changes set the go bit of DSS + */ +int omapvid_apply_changes(struct omap_vout_device *vout) +{ + int i; + struct omap_overlay *ovl; + struct omapvideo_info *ovid = &vout->vid_info; + + for (i = 0; i < ovid->num_overlays; i++) { + ovl = ovid->overlays[i]; + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + ovl->manager->apply(ovl->manager); + } + + return 0; +} + +void omap_vout_isr(void *arg, unsigned int irqstatus) +{ + int ret; + u32 addr, fid; + struct omap_overlay *ovl; + struct timeval timevalue; + struct omapvideo_info *ovid; + struct omap_dss_device *cur_display; + struct omap_vout_device *vout = (struct omap_vout_device *)arg; + + if (!vout->streaming) + return; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) + return; + + cur_display = ovl->manager->device; + + spin_lock(&vout->vbq_lock); + do_gettimeofday(&timevalue); + if (cur_display->type == OMAP_DISPLAY_TYPE_DPI) { + if (!(irqstatus & DISPC_IRQ_VSYNC)) + goto vout_isr_err; + + if (!vout->first_int && (vout->cur_frm != vout->next_frm)) { + vout->cur_frm->ts = timevalue; + vout->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->cur_frm->done); + vout->cur_frm = vout->next_frm; + } + vout->first_int = 0; + if (list_empty(&vout->dma_queue)) + goto vout_isr_err; + + vout->next_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vout->next_frm->queue); + + vout->next_frm->state = VIDEOBUF_ACTIVE; + + addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i] + + vout->cropped_offset; + + /* First save the configuration in ovelray structure */ + ret = omapvid_init(vout, addr); + if (ret) + printk(KERN_ERR VOUT_NAME + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + ret = omapvid_apply_changes(vout); + if (ret) + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + } else { + + if (vout->first_int) { + vout->first_int = 0; + goto vout_isr_err; + } + if (irqstatus & DISPC_IRQ_EVSYNC_ODD) + fid = 1; + else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN) + fid = 0; + else + goto vout_isr_err; + + vout->field_id ^= 1; + if (fid != vout->field_id) { + if (0 == fid) + vout->field_id = fid; + + goto vout_isr_err; + } + if (0 == fid) { + if (vout->cur_frm == vout->next_frm) + goto vout_isr_err; + + vout->cur_frm->ts = timevalue; + vout->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->cur_frm->done); + vout->cur_frm = vout->next_frm; + } else if (1 == fid) { + if (list_empty(&vout->dma_queue) || + (vout->cur_frm != vout->next_frm)) + goto vout_isr_err; + + vout->next_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vout->next_frm->queue); + + vout->next_frm->state = VIDEOBUF_ACTIVE; + addr = (unsigned long) + vout->queued_buf_addr[vout->next_frm->i] + + vout->cropped_offset; + /* First save the configuration in ovelray structure */ + ret = omapvid_init(vout, addr); + if (ret) + printk(KERN_ERR VOUT_NAME + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + ret = omapvid_apply_changes(vout); + if (ret) + printk(KERN_ERR VOUT_NAME + "failed to change mode\n"); + } + + } + +vout_isr_err: + spin_unlock(&vout->vbq_lock); +} + + +/* Video buffer call backs */ + +/* + * Buffer setup function is called by videobuf layer when REQBUF ioctl is + * called. This is used to setup buffers and return size and count of + * buffers allocated. After the call to this buffer, videobuf layer will + * setup buffer queue depending on the size and count of buffers + */ +static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + int startindex = 0, i, j; + u32 phy_addr = 0, virt_addr = 0; + struct omap_vout_device *vout = q->priv_data; + + if (!vout) + return -EINVAL; + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) + return -EINVAL; + + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex) + *count = startindex; + + if ((rotation_enabled(vout)) && *count > VRFB_NUM_BUFS) + *count = VRFB_NUM_BUFS; + + /* If rotation is enabled, allocate memory for VRFB space also */ + if (rotation_enabled(vout)) + if (omap_vout_vrfb_buffer_setup(vout, count, startindex)) + return -ENOMEM; + + if (V4L2_MEMORY_MMAP != vout->memory) + return 0; + + /* Now allocated the V4L2 buffers */ + *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp); + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + + for (i = startindex; i < *count; i++) { + vout->buffer_size = *size; + + virt_addr = omap_vout_alloc_buffer(vout->buffer_size, + &phy_addr); + if (!virt_addr) { + if (!rotation_enabled(vout)) + break; + /* Free the VRFB buffers if no space for V4L2 buffers */ + for (j = i; j < *count; j++) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } + } + vout->buf_virt_addr[i] = virt_addr; + vout->buf_phy_addr[i] = phy_addr; + } + *count = vout->buffer_allocated = i; + + return 0; +} + +/* + * Free the V4L2 buffers additionally allocated than default + * number of buffers and free all the VRFB buffers + */ +static void omap_vout_free_allbuffers(struct omap_vout_device *vout) +{ + int num_buffers = 0, i; + + num_buffers = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + + for (i = num_buffers; i < vout->buffer_allocated; i++) { + if (vout->buf_virt_addr[i]) + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buffer_size); + + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + /* Free the VRFB buffers only if they are allocated + * during reqbufs. Don't free if init time allocated + */ + if (!vout->vrfb_static_allocation) { + for (i = 0; i < VRFB_NUM_BUFS; i++) { + if (vout->smsshado_virt_addr[i]) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[i], + vout->smsshado_size); + vout->smsshado_virt_addr[i] = 0; + vout->smsshado_phy_addr[i] = 0; + } + } + } + vout->buffer_allocated = num_buffers; +} + +/* + * This function will be called when VIDIOC_QBUF ioctl is called. + * It prepare buffers before give out for the display. This function + * converts user space virtual address into physical address if userptr memory + * exchange mechanism is used. If rotation is enabled, it copies entire + * buffer into VRFB memory space before giving it to the DSS. + */ +static int omap_vout_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vid_vrfb_dma *tx; + enum dss_rotation rotation; + struct videobuf_dmabuf *dmabuf = NULL; + struct omap_vout_device *vout = q->priv_data; + u32 dest_frame_index = 0, src_element_index = 0; + u32 dest_element_index = 0, src_frame_index = 0; + u32 elem_count = 0, frame_count = 0, pixsize = 2; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vout->pix.width; + vb->height = vout->pix.height; + vb->size = vb->width * vb->height * vout->bpp; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + /* if user pointer memory mechanism is used, get the physical + * address of the buffer + */ + if (V4L2_MEMORY_USERPTR == vb->memory) { + if (0 == vb->baddr) + return -EINVAL; + /* Virtual address */ + /* priv points to struct videobuf_pci_sg_memory. But we went + * pointer to videobuf_dmabuf, which is member of + * videobuf_pci_sg_memory */ + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + dmabuf->vmalloc = (void *) vb->baddr; + + /* Physical address */ + dmabuf->bus_addr = + (dma_addr_t) omap_vout_uservirt_to_phys(vb->baddr); + } + + if (!rotation_enabled(vout)) { + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + vout->queued_buf_addr[vb->i] = (u8 *) dmabuf->bus_addr; + return 0; + } + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + /* If rotation is enabled, copy input buffer into VRFB + * memory space using DMA. We are copying input buffer + * into VRFB memory space of desired angle and DSS will + * read image VRFB memory for 0 degree angle + */ + pixsize = vout->bpp * vout->vrfb_bpp; + /* + * DMA transfer in double index mode + */ + + /* Frame index */ + dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) - + (vout->pix.width * vout->bpp)) + 1; + + /* Source and destination parameters */ + src_element_index = 0; + src_frame_index = 0; + dest_element_index = 1; + /* Number of elements per frame */ + elem_count = vout->pix.width * vout->bpp; + frame_count = vout->pix.height; + tx = &vout->vrfb_dma_tx; + tx->tx_status = 0; + omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32, + (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT, + tx->dev_id, 0x0); + /* src_port required only for OMAP1 */ + omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dmabuf->bus_addr, src_element_index, src_frame_index); + /*set dma source burst mode for VRFB */ + omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); + rotation = calc_rotation(vout); + + /* dest_port required only for OMAP1 */ + omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX, + vout->vrfb_context[vb->i].paddr[0], dest_element_index, + dest_frame_index); + /*set dma dest burst mode for VRFB */ + omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); + omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0); + + omap_start_dma(tx->dma_ch); + interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT); + + if (tx->tx_status == 0) { + omap_stop_dma(tx->dma_ch); + return -EINVAL; + } + /* Store buffers physical address into an array. Addresses + * from this array will be used to configure DSS */ + vout->queued_buf_addr[vb->i] = (u8 *) + vout->vrfb_context[vb->i].paddr[rotation]; + return 0; +} + +/* + * Buffer queue funtion will be called from the videobuf layer when _QBUF + * ioctl is called. It is used to enqueue buffer, which is ready to be + * displayed. + */ +static void omap_vout_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct omap_vout_device *vout = q->priv_data; + + /* Driver is also maintainig a queue. So enqueue buffer in the driver + * queue */ + list_add_tail(&vb->queue, &vout->dma_queue); + + vb->state = VIDEOBUF_QUEUED; +} + +/* + * Buffer release function is called from videobuf layer to release buffer + * which are already allocated + */ +static void omap_vout_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct omap_vout_device *vout = q->priv_data; + + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != vout->memory) + return; +} + +/* + * File operations + */ +static void omap_vout_vm_open(struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = vma->vm_private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count++; +} + +static void omap_vout_vm_close(struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = vma->vm_private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count--; +} + +static struct vm_operations_struct omap_vout_vm_ops = { + .open = omap_vout_vm_open, + .close = omap_vout_vm_close, +}; + +static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma) +{ + int i; + void *pos; + unsigned long start = vma->vm_start; + unsigned long size = (vma->vm_end - vma->vm_start); + struct videobuf_dmabuf *dmabuf = NULL; + struct omap_vout_device *vout = file->private_data; + struct videobuf_queue *q = &vout->vbq; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__, + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* look for the buffer to map */ + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + if (V4L2_MEMORY_MMAP != q->bufs[i]->memory) + continue; + if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + break; + } + + if (VIDEO_MAX_FRAME == i) { + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); + return -EINVAL; + } + q->bufs[i]->baddr = vma->vm_start; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &omap_vout_vm_ops; + vma->vm_private_data = (void *) vout; + dmabuf = videobuf_to_dma(q->bufs[i]); + pos = dmabuf->vmalloc; + vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT; + while (size > 0) { + unsigned long pfn; + pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT; + if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + vout->mmap_count++; + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + + return 0; +} + +static int omap_vout_release(struct file *file) +{ + unsigned int ret, i; + struct videobuf_queue *q; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = file->private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); + ovid = &vout->vid_info; + + if (!vout) + return 0; + + q = &vout->vbq; + /* Disable all the overlay managers connected with this interface */ + for (i = 0; i < ovid->num_overlays; i++) { + struct omap_overlay *ovl = ovid->overlays[i]; + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + ovl->get_overlay_info(ovl, &info); + info.enabled = 0; + ovl->set_overlay_info(ovl, &info); + } + } + /* Turn off the pipeline */ + ret = omapvid_apply_changes(vout); + if (ret) + v4l2_warn(&vout->vid_dev->v4l2_dev, + "Unable to apply changes\n"); + + /* Free all buffers */ + omap_vout_free_allbuffers(vout); + videobuf_mmap_free(q); + + /* Even if apply changes fails we should continue + freeing allocated memeory */ + if (vout->streaming) { + u32 mask = 0; + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | + DISPC_IRQ_EVSYNC_ODD; + omap_dispc_unregister_isr(omap_vout_isr, vout, mask); + vout->streaming = 0; + + videobuf_streamoff(q); + videobuf_queue_cancel(q); + } + + if (vout->mmap_count != 0) + vout->mmap_count = 0; + + vout->opened -= 1; + file->private_data = NULL; + + if (vout->buffer_allocated) + videobuf_mmap_free(q); + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return ret; +} + +static int omap_vout_open(struct file *file) +{ + struct videobuf_queue *q; + struct omap_vout_device *vout = NULL; + + vout = video_drvdata(file); + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); + + if (vout == NULL) + return -ENODEV; + + /* for now, we only support single open */ + if (vout->opened) + return -EBUSY; + + vout->opened += 1; + + file->private_data = vout; + vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + q = &vout->vbq; + video_vbq_ops.buf_setup = omap_vout_buffer_setup; + video_vbq_ops.buf_prepare = omap_vout_buffer_prepare; + video_vbq_ops.buf_release = omap_vout_buffer_release; + video_vbq_ops.buf_queue = omap_vout_buffer_queue; + spin_lock_init(&vout->vbq_lock); + + videobuf_queue_sg_init(q, &video_vbq_ops, NULL, &vout->vbq_lock, + vout->type, V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vout); + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return 0; +} + +/* + * V4L2 ioctls + */ +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct omap_vout_device *vout = fh; + + strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); + strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) + return -EINVAL; + + fmt->flags = omap_formats[index].flags; + strlcpy(fmt->description, omap_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = omap_formats[index].pixelformat; + + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + + f->fmt.pix = vout->pix; + return 0; + +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_video_timings *timing; + struct omap_vout_device *vout = fh; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + /* get the display device attached to the overlay */ + timing = &ovl->manager->device->panel.timings; + + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + + omap_vout_try_format(&f->fmt.pix); + return 0; +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret, bpp; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_video_timings *timing; + struct omap_vout_device *vout = fh; + + if (vout->streaming) + return -EBUSY; + + mutex_lock(&vout->lock); + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) { + ret = -EINVAL; + goto s_fmt_vid_out_exit; + } + timing = &ovl->manager->device->panel.timings; + + /* We dont support RGB24-packed mode if vrfb rotation + * is enabled*/ + if ((rotation_enabled(vout)) && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { + ret = -EINVAL; + goto s_fmt_vid_out_exit; + } + + /* get the framebuffer parameters */ + + if (rotate_90_or_270(vout)) { + vout->fbuf.fmt.height = timing->x_res; + vout->fbuf.fmt.width = timing->y_res; + } else { + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + } + + /* change to samller size is OK */ + + bpp = omap_vout_try_format(&f->fmt.pix); + f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp; + + /* try & set the new output format */ + vout->bpp = bpp; + vout->pix = f->fmt.pix; + vout->vrfb_bpp = 1; + + /* If YUYV then vrfb bpp is 2, for others its 1 */ + if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat || + V4L2_PIX_FMT_UYVY == vout->pix.pixelformat) + vout->vrfb_bpp = 2; + + /* set default crop and win */ + omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win); + + /* Save the changes in the overlay strcuture */ + ret = omapvid_init(vout, 0); + if (ret) { + v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n"); + goto s_fmt_vid_out_exit; + } + + ret = 0; + +s_fmt_vid_out_exit: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret = 0; + struct omap_vout_device *vout = fh; + struct v4l2_window *win = &f->fmt.win; + + ret = omap_vout_try_window(&vout->fbuf, win); + + if (!ret) { + if (vout->vid == OMAP_VIDEO1) + win->global_alpha = 255; + else + win->global_alpha = f->fmt.win.global_alpha; + } + + return ret; +} + +static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret = 0; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct v4l2_window *win = &f->fmt.win; + + mutex_lock(&vout->lock); + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win); + if (!ret) { + /* Video1 plane does not support global alpha */ + if (ovl->id == OMAP_DSS_VIDEO1) + vout->win.global_alpha = 255; + else + vout->win.global_alpha = f->fmt.win.global_alpha; + + vout->win.chromakey = f->fmt.win.chromakey; + } + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) + return -EINVAL; + + fmt->flags = omap_formats[index].flags; + strlcpy(fmt->description, omap_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = omap_formats[index].pixelformat; + return 0; +} + +static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + u32 key_value = 0; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + struct v4l2_window *win = &f->fmt.win; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + win->w = vout->win.w; + win->field = vout->win.field; + win->global_alpha = vout->win.global_alpha; + + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + key_value = info.trans_key; + } + win->chromakey = key_value; + return 0; +} + +static int vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct omap_vout_device *vout = fh; + struct v4l2_pix_format *pix = &vout->pix; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* Width and height are always even */ + cropcap->bounds.width = pix->width & ~1; + cropcap->bounds.height = pix->height & ~1; + + omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect); + cropcap->pixelaspect.numerator = 1; + cropcap->pixelaspect.denominator = 1; + return 0; +} + +static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct omap_vout_device *vout = fh; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + crop->c = vout->crop; + return 0; +} + +static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + int ret = -EINVAL; + struct omap_vout_device *vout = fh; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_video_timings *timing; + + if (vout->streaming) + return -EBUSY; + + mutex_lock(&vout->lock); + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + if (!ovl->manager || !ovl->manager->device) { + ret = -EINVAL; + goto s_crop_err; + } + /* get the display device attached to the overlay */ + timing = &ovl->manager->device->panel.timings; + + if (rotate_90_or_270(vout)) { + vout->fbuf.fmt.height = timing->x_res; + vout->fbuf.fmt.width = timing->y_res; + } else { + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + } + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, + &vout->fbuf, &crop->c); + +s_crop_err: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); + break; + case V4L2_CID_BG_COLOR: + ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0); + break; + case V4L2_CID_VFLIP: + ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + break; + default: + ctrl->name[0] = '\0'; + ret = -EINVAL; + } + return ret; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + int ret = 0; + struct omap_vout_device *vout = fh; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ctrl->value = vout->control[0].value; + break; + case V4L2_CID_BG_COLOR: + { + struct omap_overlay_manager_info info; + struct omap_overlay *ovl; + + ovl = vout->vid_info.overlays[0]; + if (!ovl->manager || !ovl->manager->get_manager_info) { + ret = -EINVAL; + break; + } + + ovl->manager->get_manager_info(ovl->manager, &info); + ctrl->value = info.default_color; + break; + } + case V4L2_CID_VFLIP: + ctrl->value = vout->control[2].value; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +{ + int ret = 0; + struct omap_vout_device *vout = fh; + + switch (a->id) { + case V4L2_CID_ROTATE: + { + int rotation = a->value; + + mutex_lock(&vout->lock); + + if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + if (v4l2_rot_to_dss_rot(rotation, &vout->rotation, + vout->mirror)) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + vout->control[0].value = rotation; + mutex_unlock(&vout->lock); + break; + } + case V4L2_CID_BG_COLOR: + { + struct omap_overlay *ovl; + unsigned int color = a->value; + struct omap_overlay_manager_info info; + + ovl = vout->vid_info.overlays[0]; + + mutex_lock(&vout->lock); + if (!ovl->manager || !ovl->manager->get_manager_info) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + ovl->manager->get_manager_info(ovl->manager, &info); + info.default_color = color; + if (ovl->manager->set_manager_info(ovl->manager, &info)) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + vout->control[1].value = color; + mutex_unlock(&vout->lock); + break; + } + case V4L2_CID_VFLIP: + { + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + unsigned int mirror = a->value; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + mutex_lock(&vout->lock); + + if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + vout->mirror = mirror; + vout->control[2].value = mirror; + mutex_unlock(&vout->lock); + break; + } + default: + ret = -EINVAL; + } + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int ret = 0; + unsigned int i, num_buffers = 0; + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + struct videobuf_dmabuf *dmabuf = NULL; + + if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0)) + return -EINVAL; + /* if memory is not mmp or userptr + return error */ + if ((V4L2_MEMORY_MMAP != req->memory) && + (V4L2_MEMORY_USERPTR != req->memory)) + return -EINVAL; + + mutex_lock(&vout->lock); + /* Cannot be requested when streaming is on */ + if (vout->streaming) { + ret = -EBUSY; + goto reqbuf_err; + } + + /* If buffers are already allocated free them */ + if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) { + if (vout->mmap_count) { + ret = -EBUSY; + goto reqbuf_err; + } + num_buffers = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + for (i = num_buffers; i < vout->buffer_allocated; i++) { + dmabuf = videobuf_to_dma(q->bufs[i]); + omap_vout_free_buffer((u32)dmabuf->vmalloc, + vout->buffer_size); + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + vout->buffer_allocated = num_buffers; + videobuf_mmap_free(q); + } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) { + if (vout->buffer_allocated) { + videobuf_mmap_free(q); + for (i = 0; i < vout->buffer_allocated; i++) { + kfree(q->bufs[i]); + q->bufs[i] = NULL; + } + vout->buffer_allocated = 0; + } + } + + /*store the memory type in data structure */ + vout->memory = req->memory; + + INIT_LIST_HEAD(&vout->dma_queue); + + /* call videobuf_reqbufs api */ + ret = videobuf_reqbufs(q, req); + if (ret < 0) + goto reqbuf_err; + + vout->buffer_allocated = req->count; + for (i = 0; i < req->count; i++) { + dmabuf = videobuf_to_dma(q->bufs[i]); + dmabuf->vmalloc = (void *) vout->buf_virt_addr[i]; + dmabuf->bus_addr = (dma_addr_t) vout->buf_phy_addr[i]; + dmabuf->sglen = 1; + } +reqbuf_err: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct omap_vout_device *vout = fh; + + return videobuf_querybuf(&vout->vbq, b); +} + +static int vidioc_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buffer) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + + if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) || + (buffer->index >= vout->buffer_allocated) || + (q->bufs[buffer->index]->memory != buffer->memory)) { + return -EINVAL; + } + if (V4L2_MEMORY_USERPTR == buffer->memory) { + if ((buffer->length < vout->pix.sizeimage) || + (0 == buffer->m.userptr)) { + return -EINVAL; + } + } + + if ((rotation_enabled(vout)) && + vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) { + v4l2_warn(&vout->vid_dev->v4l2_dev, + "DMA Channel not allocated for Rotation\n"); + return -EINVAL; + } + + return videobuf_qbuf(q, buffer); +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + + if (!vout->streaming) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + /* Call videobuf_dqbuf for non blocking mode */ + return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1); + else + /* Call videobuf_dqbuf for blocking mode */ + return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0); +} + +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + int ret = 0, j; + u32 addr = 0, mask = 0; + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + struct omapvideo_info *ovid = &vout->vid_info; + + mutex_lock(&vout->lock); + + if (vout->streaming) { + ret = -EBUSY; + goto streamon_err; + } + + ret = videobuf_streamon(q); + if (ret) + goto streamon_err; + + if (list_empty(&vout->dma_queue)) { + ret = -EIO; + goto streamon_err1; + } + + /* Get the next frame from the buffer queue */ + vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&vout->cur_frm->queue); + /* Mark state of the current frame to active */ + vout->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vout->field_id = 0; + + /* set flag here. Next QBUF will start DMA */ + vout->streaming = 1; + + vout->first_int = 1; + + if (omap_vout_calculate_offset(vout)) { + ret = -EINVAL; + goto streamon_err1; + } + addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i] + + vout->cropped_offset; + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD; + + omap_dispc_register_isr(omap_vout_isr, vout, mask); + + for (j = 0; j < ovid->num_overlays; j++) { + struct omap_overlay *ovl = ovid->overlays[j]; + + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + ovl->get_overlay_info(ovl, &info); + info.enabled = 1; + info.paddr = addr; + if (ovl->set_overlay_info(ovl, &info)) { + ret = -EINVAL; + goto streamon_err1; + } + } + } + + /* First save the configuration in ovelray structure */ + ret = omapvid_init(vout, addr); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + ret = omapvid_apply_changes(vout); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n"); + + ret = 0; + +streamon_err1: + if (ret) + ret = videobuf_streamoff(q); +streamon_err: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + u32 mask = 0; + int ret = 0, j; + struct omap_vout_device *vout = fh; + struct omapvideo_info *ovid = &vout->vid_info; + + if (!vout->streaming) + return -EINVAL; + + vout->streaming = 0; + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD; + + omap_dispc_unregister_isr(omap_vout_isr, vout, mask); + + for (j = 0; j < ovid->num_overlays; j++) { + struct omap_overlay *ovl = ovid->overlays[j]; + + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + info.enabled = 0; + ret = ovl->set_overlay_info(ovl, &info); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, + "failed to update overlay info in streamoff\n"); + } + } + + /* Turn of the pipeline */ + ret = omapvid_apply_changes(vout); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in" + " streamoff\n"); + + INIT_LIST_HEAD(&vout->dma_queue); + ret = videobuf_streamoff(&vout->vbq); + + return ret; +} + +static int vidioc_s_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + int enable = 0; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + /* OMAP DSS doesn't support Source and Destination color + key together */ + if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) && + (a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) + return -EINVAL; + /* OMAP DSS Doesn't support the Destination color key + and alpha blending together */ + if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) && + (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA)) + return -EINVAL; + + if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; + key_type = OMAP_DSS_COLOR_KEY_VID_SRC; + } else + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY; + + if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY; + key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + } else + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY; + + if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY | + V4L2_FBUF_FLAG_SRC_CHROMAKEY)) + enable = 1; + else + enable = 0; + if (ovl->manager && ovl->manager->get_manager_info && + ovl->manager->set_manager_info) { + + ovl->manager->get_manager_info(ovl->manager, &info); + info.trans_enabled = enable; + info.trans_key_type = key_type; + info.trans_key = vout->win.chromakey; + + if (ovl->manager->set_manager_info(ovl->manager, &info)) + return -EINVAL; + } + if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + enable = 1; + } else { + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA; + enable = 0; + } + if (ovl->manager && ovl->manager->get_manager_info && + ovl->manager->set_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + info.alpha_enabled = enable; + if (ovl->manager->set_manager_info(ovl->manager, &info)) + return -EINVAL; + } + + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + a->flags = 0x0; + a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY + | V4L2_FBUF_CAP_SRC_CHROMAKEY; + + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC) + a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; + if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST) + a->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + } + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + if (info.alpha_enabled) + a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + } + + return 0; +} + +static const struct v4l2_ioctl_ops vout_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static const struct v4l2_file_operations omap_vout_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .mmap = omap_vout_mmap, + .open = omap_vout_open, + .release = omap_vout_release, +}; + +/* Init functions used during driver initialization */ +/* Initial setup of video_data */ +static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) +{ + struct video_device *vfd; + struct v4l2_pix_format *pix; + struct v4l2_control *control; + struct omap_dss_device *display = + vout->vid_info.overlays[0]->manager->device; + + /* set the default pix */ + pix = &vout->pix; + + /* Set the default picture of QVGA */ + pix->width = QQVGA_WIDTH; + pix->height = QQVGA_HEIGHT; + + /* Default pixel format is RGB 5-6-5 */ + pix->pixelformat = V4L2_PIX_FMT_RGB565; + pix->field = V4L2_FIELD_ANY; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + pix->colorspace = V4L2_COLORSPACE_JPEG; + + vout->bpp = RGB565_BPP; + vout->fbuf.fmt.width = display->panel.timings.x_res; + vout->fbuf.fmt.height = display->panel.timings.y_res; + + /* Set the data structures for the overlay parameters*/ + vout->win.global_alpha = 255; + vout->fbuf.flags = 0; + vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA | + V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY; + vout->win.chromakey = 0; + + omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win); + + /*Initialize the control variables for + rotation, flipping and background color. */ + control = vout->control; + control[0].id = V4L2_CID_ROTATE; + control[0].value = 0; + vout->rotation = 0; + vout->mirror = 0; + vout->control[2].id = V4L2_CID_HFLIP; + vout->control[2].value = 0; + vout->vrfb_bpp = 2; + + control[1].id = V4L2_CID_BG_COLOR; + control[1].value = 0; + + /* initialize the video_device struct */ + vfd = vout->vfd = video_device_alloc(); + + if (!vfd) { + printk(KERN_ERR VOUT_NAME ": could not allocate" + " video device struct\n"); + return -ENOMEM; + } + vfd->release = video_device_release; + vfd->ioctl_ops = &vout_ioctl_ops; + + strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name)); + + /* need to register for a VID_HARDWARE_* ID in videodev.h */ + vfd->fops = &omap_vout_fops; + vfd->v4l2_dev = &vout->vid_dev->v4l2_dev; + mutex_init(&vout->lock); + + vfd->minor = -1; + return 0; + +} + +/* Setup video buffers */ +static int __init omap_vout_setup_video_bufs(struct platform_device *pdev, + int vid_num) +{ + u32 numbuffers; + int ret = 0, i, j; + int image_width, image_height; + struct video_device *vfd; + struct omap_vout_device *vout; + int static_vrfb_allocation = 0, vrfb_num_bufs = VRFB_NUM_BUFS; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = + container_of(v4l2_dev, struct omap2video_device, v4l2_dev); + + vout = vid_dev->vouts[vid_num]; + vfd = vout->vfd; + + numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers; + vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize; + dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size); + + for (i = 0; i < numbuffers; i++) { + vout->buf_virt_addr[i] = + omap_vout_alloc_buffer(vout->buffer_size, + (u32 *) &vout->buf_phy_addr[i]); + if (!vout->buf_virt_addr[i]) { + numbuffers = i; + ret = -ENOMEM; + goto free_buffers; + } + } + + for (i = 0; i < VRFB_NUM_BUFS; i++) { + if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) { + dev_info(&pdev->dev, ": VRFB allocation failed\n"); + for (j = 0; j < i; j++) + omap_vrfb_release_ctx(&vout->vrfb_context[j]); + ret = -ENOMEM; + goto free_buffers; + } + } + vout->cropped_offset = 0; + + /* Calculate VRFB memory size */ + /* allocate for worst case size */ + image_width = VID_MAX_WIDTH / TILE_SIZE; + if (VID_MAX_WIDTH % TILE_SIZE) + image_width++; + + image_width = image_width * TILE_SIZE; + image_height = VID_MAX_HEIGHT / TILE_SIZE; + + if (VID_MAX_HEIGHT % TILE_SIZE) + image_height++; + + image_height = image_height * TILE_SIZE; + vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2); + + /* + * Request and Initialize DMA, for DMA based VRFB transfer + */ + vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; + vout->vrfb_dma_tx.dma_ch = -1; + vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; + ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", + omap_vout_vrfb_dma_tx_callback, + (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); + if (ret < 0) { + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + dev_info(&pdev->dev, ": failed to allocate DMA Channel for" + " video%d\n", vfd->minor); + } + init_waitqueue_head(&vout->vrfb_dma_tx.wait); + + /* Allocate VRFB buffers if selected through bootargs */ + static_vrfb_allocation = (vid_num == 0) ? + vid1_static_vrfb_alloc : vid2_static_vrfb_alloc; + + /* statically allocated the VRFB buffer is done through + commands line aruments */ + if (static_vrfb_allocation) { + if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) { + ret = -ENOMEM; + goto release_vrfb_ctx; + } + vout->vrfb_static_allocation = 1; + } + return 0; + +release_vrfb_ctx: + for (j = 0; j < VRFB_NUM_BUFS; j++) + omap_vrfb_release_ctx(&vout->vrfb_context[j]); + +free_buffers: + for (i = 0; i < numbuffers; i++) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buffer_size); + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + return ret; + +} + +/* Create video out devices */ +static int __init omap_vout_create_video_devices(struct platform_device *pdev) +{ + int ret = 0, k; + struct omap_vout_device *vout; + struct video_device *vfd = NULL; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = container_of(v4l2_dev, + struct omap2video_device, v4l2_dev); + + for (k = 0; k < pdev->num_resources; k++) { + + vout = kmalloc(sizeof(struct omap_vout_device), GFP_KERNEL); + if (!vout) { + dev_err(&pdev->dev, ": could not allocate memory\n"); + return -ENOMEM; + } + memset(vout, 0, sizeof(struct omap_vout_device)); + + vout->vid = k; + vid_dev->vouts[k] = vout; + vout->vid_dev = vid_dev; + /* Select video2 if only 1 overlay is controlled by V4L2 */ + if (pdev->num_resources == 1) + vout->vid_info.overlays[0] = vid_dev->overlays[k + 2]; + else + /* Else select video1 and video2 one by one. */ + vout->vid_info.overlays[0] = vid_dev->overlays[k + 1]; + vout->vid_info.num_overlays = 1; + vout->vid_info.id = k + 1; + + /* Setup the default configuration for the video devices + */ + if (omap_vout_setup_video_data(vout) != 0) { + ret = -ENOMEM; + goto error; + } + + /* Allocate default number of buffers for the video streaming + * and reserve the VRFB space for rotation + */ + if (omap_vout_setup_video_bufs(pdev, k) != 0) { + ret = -ENOMEM; + goto error1; + } + + /* Register the Video device with V4L2 + */ + vfd = vout->vfd; + if (video_register_device(vfd, VFL_TYPE_GRABBER, k + 1) < 0) { + dev_err(&pdev->dev, ": Could not register " + "Video for Linux device\n"); + vfd->minor = -1; + ret = -ENODEV; + goto error2; + } + video_set_drvdata(vfd, vout); + + /* Configure the overlay structure */ + ret = omapvid_init(vid_dev->vouts[k], 0); + if (!ret) + goto success; + +error2: + omap_vout_release_vrfb(vout); + omap_vout_free_buffers(vout); +error1: + video_device_release(vfd); +error: + kfree(vout); + return ret; + +success: + dev_info(&pdev->dev, ": registered and initialized" + " video device %d\n", vfd->minor); + if (k == (pdev->num_resources - 1)) + return 0; + } + + return -ENODEV; +} +/* Driver functions */ +static void omap_vout_cleanup_device(struct omap_vout_device *vout) +{ + struct video_device *vfd; + + if (!vout) + return; + + vfd = vout->vfd; + if (vfd) { + if (!video_is_registered(vfd)) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(vfd); + } else { + /* + * The unregister function will release the video_device + * struct as well as unregistering it. + */ + video_unregister_device(vfd); + } + } + + omap_vout_release_vrfb(vout); + omap_vout_free_buffers(vout); + /* Free the VRFB buffer if allocated + * init time + */ + if (vout->vrfb_static_allocation) + omap_vout_free_vrfb_buffers(vout); + + kfree(vout); +} + +static int omap_vout_remove(struct platform_device *pdev) +{ + int k; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = container_of(v4l2_dev, struct + omap2video_device, v4l2_dev); + + v4l2_device_unregister(v4l2_dev); + for (k = 0; k < pdev->num_resources; k++) + omap_vout_cleanup_device(vid_dev->vouts[k]); + + for (k = 0; k < vid_dev->num_displays; k++) { + if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED) + vid_dev->displays[k]->disable(vid_dev->displays[k]); + + omap_dss_put_device(vid_dev->displays[k]); + } + kfree(vid_dev); + return 0; +} + +static int __init omap_vout_probe(struct platform_device *pdev) +{ + int ret = 0, i; + struct omap_overlay *ovl; + struct omap_dss_device *dssdev = NULL; + struct omap_dss_device *def_display; + struct omap2video_device *vid_dev = NULL; + + if (pdev->num_resources == 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + return -ENODEV; + } + + vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL); + if (vid_dev == NULL) + return -ENOMEM; + + vid_dev->num_displays = 0; + for_each_dss_dev(dssdev) { + omap_dss_get_device(dssdev); + vid_dev->displays[vid_dev->num_displays++] = dssdev; + } + + if (vid_dev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + ret = -EINVAL; + goto probe_err0; + } + + vid_dev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < vid_dev->num_overlays; i++) + vid_dev->overlays[i] = omap_dss_get_overlay(i); + + vid_dev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < vid_dev->num_managers; i++) + vid_dev->managers[i] = omap_dss_get_overlay_manager(i); + + /* Get the Video1 overlay and video2 overlay. + * Setup the Display attached to that overlays + */ + for (i = 1; i < vid_dev->num_overlays; i++) { + ovl = omap_dss_get_overlay(i); + if (ovl->manager && ovl->manager->device) { + def_display = ovl->manager->device; + } else { + dev_warn(&pdev->dev, "cannot find display\n"); + def_display = NULL; + } + if (def_display) { + ret = def_display->enable(def_display); + if (ret) { + /* Here we are not considering a error + * as display may be enabled by frame + * buffer driver + */ + dev_warn(&pdev->dev, + "'%s' Display already enabled\n", + def_display->name); + } + /* set the update mode */ + if (def_display->caps & + OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->enable_te) + def_display->enable_te(def_display, 1); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); +#else /* MANUAL_UPDATE */ + if (def_display->enable_te) + def_display->enable_te(def_display, 0); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_MANUAL); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + } + } + + if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) { + dev_err(&pdev->dev, "v4l2_device_register failed\n"); + ret = -ENODEV; + goto probe_err1; + } + + ret = omap_vout_create_video_devices(pdev); + if (ret) + goto probe_err2; + + for (i = 0; i < vid_dev->num_displays; i++) { + struct omap_dss_device *display = vid_dev->displays[i]; + + if (display->update) + display->update(display, 0, 0, + display->panel.timings.x_res, + display->panel.timings.y_res); + } + return 0; + +probe_err2: + v4l2_device_unregister(&vid_dev->v4l2_dev); +probe_err1: + for (i = 1; i < vid_dev->num_overlays; i++) { + def_display = NULL; + ovl = omap_dss_get_overlay(i); + if (ovl->manager && ovl->manager->device) + def_display = ovl->manager->device; + + if (def_display) + def_display->disable(def_display); + } +probe_err0: + kfree(vid_dev); + return ret; +} + +static struct platform_driver omap_vout_driver = { + .driver = { + .name = VOUT_NAME, + }, + .probe = omap_vout_probe, + .remove = omap_vout_remove, +}; + +static int __init omap_vout_init(void) +{ + if (platform_driver_register(&omap_vout_driver) != 0) { + printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); + return -EINVAL; + } + return 0; +} + +static void omap_vout_cleanup(void) +{ + platform_driver_unregister(&omap_vout_driver); +} + +late_initcall(omap_vout_init); +module_exit(omap_vout_cleanup); diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/video/omap/omap_voutdef.h new file mode 100644 index 00000000000..ea3a047f8bc --- /dev/null +++ b/drivers/media/video/omap/omap_voutdef.h @@ -0,0 +1,147 @@ +/* + * omap_voutdef.h + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef OMAP_VOUTDEF_H +#define OMAP_VOUTDEF_H + +#include <plat/display.h> + +#define YUYV_BPP 2 +#define RGB565_BPP 2 +#define RGB24_BPP 3 +#define RGB32_BPP 4 +#define TILE_SIZE 32 +#define YUYV_VRFB_BPP 2 +#define RGB_VRFB_BPP 1 +#define MAX_CID 3 +#define MAC_VRFB_CTXS 4 +#define MAX_VOUT_DEV 2 +#define MAX_OVLS 3 +#define MAX_DISPLAYS 3 +#define MAX_MANAGERS 3 + +/* Enum for Rotation + * DSS understands rotation in 0, 1, 2, 3 context + * while V4L2 driver understands it as 0, 90, 180, 270 + */ +enum dss_rotation { + dss_rotation_0_degree = 0, + dss_rotation_90_degree = 1, + dss_rotation_180_degree = 2, + dss_rotation_270_degree = 3, +}; +/* + * This structure is used to store the DMA transfer parameters + * for VRFB hidden buffer + */ +struct vid_vrfb_dma { + int dev_id; + int dma_ch; + int req_status; + int tx_status; + wait_queue_head_t wait; +}; + +struct omapvideo_info { + int id; + int num_overlays; + struct omap_overlay *overlays[MAX_OVLS]; +}; + +struct omap2video_device { + struct mutex mtx; + + int state; + + struct v4l2_device v4l2_dev; + struct omap_vout_device *vouts[MAX_VOUT_DEV]; + + int num_displays; + struct omap_dss_device *displays[MAX_DISPLAYS]; + int num_overlays; + struct omap_overlay *overlays[MAX_OVLS]; + int num_managers; + struct omap_overlay_manager *managers[MAX_MANAGERS]; +}; + +/* per-device data structure */ +struct omap_vout_device { + + struct omapvideo_info vid_info; + struct video_device *vfd; + struct omap2video_device *vid_dev; + int vid; + int opened; + + /* we don't allow to change image fmt/size once buffer has + * been allocated + */ + int buffer_allocated; + /* allow to reuse previously allocated buffer which is big enough */ + int buffer_size; + /* keep buffer info across opens */ + unsigned long buf_virt_addr[VIDEO_MAX_FRAME]; + unsigned long buf_phy_addr[VIDEO_MAX_FRAME]; + enum omap_color_mode dss_mode; + + /* we don't allow to request new buffer when old buffers are + * still mmaped + */ + int mmap_count; + + spinlock_t vbq_lock; /* spinlock for videobuf queues */ + unsigned long field_count; /* field counter for videobuf_buffer */ + + /* non-NULL means streaming is in progress. */ + bool streaming; + + struct v4l2_pix_format pix; + struct v4l2_rect crop; + struct v4l2_window win; + struct v4l2_framebuffer fbuf; + + /* Lock to protect the shared data structures in ioctl */ + struct mutex lock; + + /* V4L2 control structure for different control id */ + struct v4l2_control control[MAX_CID]; + enum dss_rotation rotation; + bool mirror; + int flicker_filter; + /* V4L2 control structure for different control id */ + + int bpp; /* bytes per pixel */ + int vrfb_bpp; /* bytes per pixel with respect to VRFB */ + + struct vid_vrfb_dma vrfb_dma_tx; + unsigned int smsshado_phy_addr[MAC_VRFB_CTXS]; + unsigned int smsshado_virt_addr[MAC_VRFB_CTXS]; + struct vrfb vrfb_context[MAC_VRFB_CTXS]; + bool vrfb_static_allocation; + unsigned int smsshado_size; + unsigned char pos; + + int ps, vr_ps, line_length, first_int, field_id; + enum v4l2_memory memory; + struct videobuf_buffer *cur_frm, *next_frm; + struct list_head dma_queue; + u8 *queued_buf_addr[VIDEO_MAX_FRAME]; + u32 cropped_offset; + s32 tv_field1_offset; + void *isr_handle; + + /* Buffer queue variables */ + struct omap_vout_device *vout; + enum v4l2_buf_type type; + struct videobuf_queue vbq; + int io_allowed; + +}; +#endif /* ifndef OMAP_VOUTDEF_H */ diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/video/omap/omap_voutlib.c new file mode 100644 index 00000000000..b941c761eef --- /dev/null +++ b/drivers/media/video/omap/omap_voutlib.c @@ -0,0 +1,293 @@ +/* + * omap_voutlib.c + * + * Copyright (C) 2005-2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Based on the OMAP2 camera driver + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP24xx camera controller. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2010 Texas Instruments. + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <plat/cpu.h> + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP Video library"); +MODULE_LICENSE("GPL"); + +/* Return the default overlay cropping rectangle in crop given the image + * size in pix and the video display size in fbuf. The default + * cropping rectangle is the largest rectangle no larger than the capture size + * that will fit on the display. The default cropping rectangle is centered in + * the image. All dimensions and offsets are rounded down to even numbers. + */ +void omap_vout_default_crop(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop) +{ + crop->width = (pix->width < fbuf->fmt.width) ? + pix->width : fbuf->fmt.width; + crop->height = (pix->height < fbuf->fmt.height) ? + pix->height : fbuf->fmt.height; + crop->width &= ~1; + crop->height &= ~1; + crop->left = ((pix->width - crop->width) >> 1) & ~1; + crop->top = ((pix->height - crop->height) >> 1) & ~1; +} +EXPORT_SYMBOL_GPL(omap_vout_default_crop); + +/* Given a new render window in new_win, adjust the window to the + * nearest supported configuration. The adjusted window parameters are + * returned in new_win. + * Returns zero if succesful, or -EINVAL if the requested window is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_try_window(struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win) +{ + struct v4l2_rect try_win; + + /* make a working copy of the new_win rectangle */ + try_win = new_win->w; + + /* adjust the preview window so it fits on the display by clipping any + * offscreen areas + */ + if (try_win.left < 0) { + try_win.width += try_win.left; + try_win.left = 0; + } + if (try_win.top < 0) { + try_win.height += try_win.top; + try_win.top = 0; + } + try_win.width = (try_win.width < fbuf->fmt.width) ? + try_win.width : fbuf->fmt.width; + try_win.height = (try_win.height < fbuf->fmt.height) ? + try_win.height : fbuf->fmt.height; + if (try_win.left + try_win.width > fbuf->fmt.width) + try_win.width = fbuf->fmt.width - try_win.left; + if (try_win.top + try_win.height > fbuf->fmt.height) + try_win.height = fbuf->fmt.height - try_win.top; + try_win.width &= ~1; + try_win.height &= ~1; + + if (try_win.width <= 0 || try_win.height <= 0) + return -EINVAL; + + /* We now have a valid preview window, so go with it */ + new_win->w = try_win; + new_win->field = V4L2_FIELD_ANY; + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_try_window); + +/* Given a new render window in new_win, adjust the window to the + * nearest supported configuration. The image cropping window in crop + * will also be adjusted if necessary. Preference is given to keeping the + * the window as close to the requested configuration as possible. If + * successful, new_win, vout->win, and crop are updated. + * Returns zero if succesful, or -EINVAL if the requested preview window is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_new_window(struct v4l2_rect *crop, + struct v4l2_window *win, struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win) +{ + int err; + + err = omap_vout_try_window(fbuf, new_win); + if (err) + return err; + + /* update our preview window */ + win->w = new_win->w; + win->field = new_win->field; + win->chromakey = new_win->chromakey; + + /* Adjust the cropping window to allow for resizing limitation */ + if (cpu_is_omap24xx()) { + /* For 24xx limit is 8x to 1/2x scaling. */ + if ((crop->height/win->w.height) >= 2) + crop->height = win->w.height * 2; + + if ((crop->width/win->w.width) >= 2) + crop->width = win->w.width * 2; + + if (crop->width > 768) { + /* The OMAP2420 vertical resizing line buffer is 768 + * pixels wide. If the cropped image is wider than + * 768 pixels then it cannot be vertically resized. + */ + if (crop->height != win->w.height) + crop->width = 768; + } + } else if (cpu_is_omap34xx()) { + /* For 34xx limit is 8x to 1/4x scaling. */ + if ((crop->height/win->w.height) >= 4) + crop->height = win->w.height * 4; + + if ((crop->width/win->w.width) >= 4) + crop->width = win->w.width * 4; + } + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_new_window); + +/* Given a new cropping rectangle in new_crop, adjust the cropping rectangle to + * the nearest supported configuration. The image render window in win will + * also be adjusted if necessary. The preview window is adjusted such that the + * horizontal and vertical rescaling ratios stay constant. If the render + * window would fall outside the display boundaries, the cropping rectangle + * will also be adjusted to maintain the rescaling ratios. If successful, crop + * and win are updated. + * Returns zero if succesful, or -EINVAL if the requested cropping rectangle is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_new_crop(struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_window *win, + struct v4l2_framebuffer *fbuf, const struct v4l2_rect *new_crop) +{ + struct v4l2_rect try_crop; + unsigned long vresize, hresize; + + /* make a working copy of the new_crop rectangle */ + try_crop = *new_crop; + + /* adjust the cropping rectangle so it fits in the image */ + if (try_crop.left < 0) { + try_crop.width += try_crop.left; + try_crop.left = 0; + } + if (try_crop.top < 0) { + try_crop.height += try_crop.top; + try_crop.top = 0; + } + try_crop.width = (try_crop.width < pix->width) ? + try_crop.width : pix->width; + try_crop.height = (try_crop.height < pix->height) ? + try_crop.height : pix->height; + if (try_crop.left + try_crop.width > pix->width) + try_crop.width = pix->width - try_crop.left; + if (try_crop.top + try_crop.height > pix->height) + try_crop.height = pix->height - try_crop.top; + + try_crop.width &= ~1; + try_crop.height &= ~1; + + if (try_crop.width <= 0 || try_crop.height <= 0) + return -EINVAL; + + if (cpu_is_omap24xx()) { + if (crop->height != win->w.height) { + /* If we're resizing vertically, we can't support a + * crop width wider than 768 pixels. + */ + if (try_crop.width > 768) + try_crop.width = 768; + } + } + /* vertical resizing */ + vresize = (1024 * crop->height) / win->w.height; + if (cpu_is_omap24xx() && (vresize > 2048)) + vresize = 2048; + else if (cpu_is_omap34xx() && (vresize > 4096)) + vresize = 4096; + + win->w.height = ((1024 * try_crop.height) / vresize) & ~1; + if (win->w.height == 0) + win->w.height = 2; + if (win->w.height + win->w.top > fbuf->fmt.height) { + /* We made the preview window extend below the bottom of the + * display, so clip it to the display boundary and resize the + * cropping height to maintain the vertical resizing ratio. + */ + win->w.height = (fbuf->fmt.height - win->w.top) & ~1; + if (try_crop.height == 0) + try_crop.height = 2; + } + /* horizontal resizing */ + hresize = (1024 * crop->width) / win->w.width; + if (cpu_is_omap24xx() && (hresize > 2048)) + hresize = 2048; + else if (cpu_is_omap34xx() && (hresize > 4096)) + hresize = 4096; + + win->w.width = ((1024 * try_crop.width) / hresize) & ~1; + if (win->w.width == 0) + win->w.width = 2; + if (win->w.width + win->w.left > fbuf->fmt.width) { + /* We made the preview window extend past the right side of the + * display, so clip it to the display boundary and resize the + * cropping width to maintain the horizontal resizing ratio. + */ + win->w.width = (fbuf->fmt.width - win->w.left) & ~1; + if (try_crop.width == 0) + try_crop.width = 2; + } + if (cpu_is_omap24xx()) { + if ((try_crop.height/win->w.height) >= 2) + try_crop.height = win->w.height * 2; + + if ((try_crop.width/win->w.width) >= 2) + try_crop.width = win->w.width * 2; + + if (try_crop.width > 768) { + /* The OMAP2420 vertical resizing line buffer is + * 768 pixels wide. If the cropped image is wider + * than 768 pixels then it cannot be vertically resized. + */ + if (try_crop.height != win->w.height) + try_crop.width = 768; + } + } else if (cpu_is_omap34xx()) { + if ((try_crop.height/win->w.height) >= 4) + try_crop.height = win->w.height * 4; + + if ((try_crop.width/win->w.width) >= 4) + try_crop.width = win->w.width * 4; + } + /* update our cropping rectangle and we're done */ + *crop = try_crop; + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_new_crop); + +/* Given a new format in pix and fbuf, crop and win + * structures are initialized to default values. crop + * is initialized to the largest window size that will fit on the display. The + * crop window is centered in the image. win is initialized to + * the same size as crop and is centered on the display. + * All sizes and offsets are constrained to be even numbers. + */ +void omap_vout_new_format(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop, + struct v4l2_window *win) +{ + /* crop defines the preview source window in the image capture + * buffer + */ + omap_vout_default_crop(pix, fbuf, crop); + + /* win defines the preview target window on the display */ + win->w.width = crop->width; + win->w.height = crop->height; + win->w.left = ((fbuf->fmt.width - win->w.width) >> 1) & ~1; + win->w.top = ((fbuf->fmt.height - win->w.height) >> 1) & ~1; +} +EXPORT_SYMBOL_GPL(omap_vout_new_format); + diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/video/omap/omap_voutlib.h new file mode 100644 index 00000000000..a60b16e8bfc --- /dev/null +++ b/drivers/media/video/omap/omap_voutlib.h @@ -0,0 +1,34 @@ +/* + * omap_voutlib.h + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef OMAP_VOUTLIB_H +#define OMAP_VOUTLIB_H + +extern void omap_vout_default_crop(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop); + +extern int omap_vout_new_crop(struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_window *win, + struct v4l2_framebuffer *fbuf, + const struct v4l2_rect *new_crop); + +extern int omap_vout_try_window(struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win); + +extern int omap_vout_new_window(struct v4l2_rect *crop, + struct v4l2_window *win, struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win); + +extern void omap_vout_new_format(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop, + struct v4l2_window *win); +#endif /* #ifndef OMAP_VOUTLIB_H */ + diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index ce76d952e16..f85b2ed8a2d 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -452,8 +452,8 @@ static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, *size = fh->pix.sizeimage; /* accessing fh->cam->capture_mem is ok, it's constant */ - while (*size * *cnt > fh->cam->capture_mem) - (*cnt)--; + if (*size * *cnt > fh->cam->capture_mem) + *cnt = fh->cam->capture_mem / *size; return 0; } diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index e0bce8dc74b..a10912097b7 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -57,8 +57,8 @@ #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>" +& 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 @@ -5916,11 +5916,6 @@ ov51x_disconnect(struct usb_interface *intf) mutex_lock(&ov->lock); usb_set_intfdata (intf, NULL); - if (!ov) { - mutex_unlock(&ov->lock); - return; - } - /* Free device number */ ov511_devused &= ~(1 << ov->nr); @@ -5945,7 +5940,7 @@ ov51x_disconnect(struct usb_interface *intf) ov->dev = NULL; /* Free the memory */ - if (ov && !ov->user) { + if (!ov->user) { mutex_lock(&ov->cbuf_lock); kfree(ov->cbuf); ov->cbuf = NULL; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index aaa50f9b8e7..91c886ab15c 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -198,6 +198,7 @@ struct ov7670_info { struct ov7670_format_struct *fmt; /* Current format */ unsigned char sat; /* Saturation value */ int hue; /* Hue value */ + u8 clkrc; /* Clock divider value */ }; static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) @@ -351,7 +352,7 @@ static struct regval_list ov7670_default_regs[] = { static struct regval_list ov7670_fmt_yuv422[] = { { REG_COM7, 0x0 }, /* Selects YUV mode */ { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0 }, + { REG_COM1, 0 }, /* CCIR601 */ { REG_COM15, COM15_R00FF }, { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ @@ -367,7 +368,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { static struct regval_list ov7670_fmt_rgb565[] = { { REG_COM7, COM7_RGB }, /* Selects RGB mode */ { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0x0 }, + { REG_COM1, 0x0 }, /* CCIR601 */ { REG_COM15, COM15_RGB565 }, { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ @@ -383,7 +384,7 @@ static struct regval_list ov7670_fmt_rgb565[] = { static struct regval_list ov7670_fmt_rgb444[] = { { REG_COM7, COM7_RGB }, /* Selects RGB mode */ { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ - { REG_COM1, 0x40 }, /* Magic reserved bit */ + { REG_COM1, 0x0 }, /* CCIR601 */ { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ @@ -408,8 +409,13 @@ static struct regval_list ov7670_fmt_raw[] = { /* * Low-level register I/O. + * + * Note that there are two versions of these. On the XO 1, the + * i2c controller only does SMBUS, so that's what we use. The + * ov7670 is not really an SMBUS device, though, so the communication + * is not always entirely reliable. */ - +#ifdef CONFIG_OLPC_XO_1 static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, unsigned char *value) { @@ -432,9 +438,67 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, int ret = i2c_smbus_write_byte_data(client, reg, value); if (reg == REG_COM7 && (value & COM7_RESET)) - msleep(2); /* Wait for reset to run */ + msleep(5); /* Wait for reset to run */ + return ret; +} + +#else /* ! CONFIG_OLPC_XO_1 */ +/* + * On most platforms, we'd rather do straight i2c I/O. + */ +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 data = reg; + struct i2c_msg msg; + int ret; + + /* + * Send out the register address... + */ + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + msg.buf = &data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + printk(KERN_ERR "Error %d on register write\n", ret); + return ret; + } + /* + * ...then read back the result. + */ + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) { + *value = data; + ret = 0; + } + return ret; +} + + +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg; + unsigned char data[2] = { reg, value }; + int ret; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret > 0) + ret = 0; + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(5); /* Wait for reset to run */ return ret; } +#endif /* CONFIG_OLPC_XO_1 */ /* @@ -744,22 +808,12 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) struct ov7670_format_struct *ovfmt; struct ov7670_win_size *wsize; struct ov7670_info *info = to_state(sd); - unsigned char com7, clkrc = 0; + unsigned char com7; ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); if (ret) return ret; /* - * HACK: if we're running rgb565 we need to grab then rewrite - * CLKRC. If we're *not*, however, then rewriting clkrc hoses - * the colors. - */ - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { - ret = ov7670_read(sd, REG_CLKRC, &clkrc); - if (ret) - return ret; - } - /* * COM7 is a pain in the ass, it doesn't like to be read then * quickly written afterward. But we have everything we need * to set it absolutely here, as long as the format-specific @@ -779,8 +833,18 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) ret = ov7670_write_array(sd, wsize->regs); info->fmt = ovfmt; - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0) - ret = ov7670_write(sd, REG_CLKRC, clkrc); + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (ret == 0) + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); return ret; } @@ -791,20 +855,17 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; - unsigned char clkrc; - int ret; + struct ov7670_info *info = to_state(sd); if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ov7670_read(sd, REG_CLKRC, &clkrc); - if (ret < 0) - return ret; + memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; cp->timeperframe.numerator = 1; cp->timeperframe.denominator = OV7670_FRAME_RATE; - if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); return 0; } @@ -812,19 +873,14 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; - unsigned char clkrc; - int ret, div; + struct ov7670_info *info = to_state(sd); + int div; if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (cp->extendedmode != 0) return -EINVAL; - /* - * CLKRC has a reserved bit, so let's preserve it. - */ - ret = ov7670_read(sd, REG_CLKRC, &clkrc); - if (ret < 0) - return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ else @@ -833,10 +889,10 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) div = 1; else if (div > CLK_SCALE) div = CLK_SCALE; - clkrc = (clkrc & 0x80) | div; + info->clkrc = (info->clkrc & 0x80) | div; tpf->numerator = 1; tpf->denominator = OV7670_FRAME_RATE/div; - return ov7670_write(sd, REG_CLKRC, clkrc); + return ov7670_write(sd, REG_CLKRC, info->clkrc); } @@ -1115,6 +1171,140 @@ static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) return ret; } +/* + * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes + * the data sheet, the VREF parts should be the most significant, but + * experience shows otherwise. There seems to be little value in + * messing with the VREF bits, so we leave them alone. + */ +static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char gain; + + ret = ov7670_read(sd, REG_GAIN, &gain); + *value = gain; + return ret; +} + +static int ov7670_s_gain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_write(sd, REG_GAIN, value & 0xff); + /* Have to turn off AGC as well */ + if (ret == 0) { + ret = ov7670_read(sd, REG_COM8, &com8); + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); + } + return ret; +} + +/* + * Tweak autogain. + */ +static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + *value = (com8 & COM8_AGC) != 0; + return ret; +} + +static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value) + com8 |= COM8_AGC; + else + com8 &= ~COM8_AGC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + +/* + * Exposure is spread all over the place: top 6 bits in AECHH, middle + * 8 in AECH, and two stashed in COM1 just for the hell of it. + */ +static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com1, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_AECH, &aech) + + ov7670_read(sd, REG_AECHH, &aechh); + *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); + return ret; +} + +static int ov7670_s_exp(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com1, com8, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_COM8, &com8); + ov7670_read(sd, REG_AECHH, &aechh); + if (ret) + return ret; + + com1 = (com1 & 0xfc) | (value & 0x03); + aech = (value >> 2) & 0xff; + aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); + ret = ov7670_write(sd, REG_COM1, com1) + + ov7670_write(sd, REG_AECH, aech) + + ov7670_write(sd, REG_AECHH, aechh); + /* Have to turn off AEC as well */ + if (ret == 0) + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); + return ret; +} + +/* + * Tweak autoexposure. + */ +static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (com8 & COM8_AEC) + *atype = V4L2_EXPOSURE_AUTO; + else + *atype = V4L2_EXPOSURE_MANUAL; + return ret; +} + +static int ov7670_s_autoexp(struct v4l2_subdev *sd, + enum v4l2_exposure_auto_type value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value == V4L2_EXPOSURE_AUTO) + com8 |= COM8_AEC; + else + com8 &= ~COM8_AEC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + + + static int ov7670_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { @@ -1131,6 +1321,14 @@ static int ov7670_queryctrl(struct v4l2_subdev *sd, return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); case V4L2_CID_HUE: return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); + case V4L2_CID_GAIN: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_AUTOGAIN: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + case V4L2_CID_EXPOSURE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); + case V4L2_CID_EXPOSURE_AUTO: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); } return -EINVAL; } @@ -1150,6 +1348,14 @@ static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return ov7670_g_vflip(sd, &ctrl->value); case V4L2_CID_HFLIP: return ov7670_g_hflip(sd, &ctrl->value); + case V4L2_CID_GAIN: + return ov7670_g_gain(sd, &ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_g_autogain(sd, &ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_g_exp(sd, &ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_g_autoexp(sd, &ctrl->value); } return -EINVAL; } @@ -1169,6 +1375,15 @@ static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return ov7670_s_vflip(sd, ctrl->value); case V4L2_CID_HFLIP: return ov7670_s_hflip(sd, ctrl->value); + case V4L2_CID_GAIN: + return ov7670_s_gain(sd, ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_s_autogain(sd, ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_s_exp(sd, ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_s_autoexp(sd, + (enum v4l2_exposure_auto_type) ctrl->value); } return -EINVAL; } @@ -1268,6 +1483,7 @@ static int ov7670_probe(struct i2c_client *client, info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ + info->clkrc = 1; /* 30fps */ return 0; } diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 47bf60ceb7a..36599a65f54 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -59,9 +59,9 @@ static const struct ov9640_reg ov9640_regs_dflt[] = { * COM12 |= OV9640_COM12_YUV_AVG * * for RGB, alter the following registers: - * COM7 |= OV9640_COM7_RGB - * COM13 |= OV9640_COM13_RGB_AVG - * COM15 |= proper RGB color encoding mode + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode */ static const struct ov9640_reg ov9640_regs_qqcif[] = { { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 0598bbd3f36..7129b50757d 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -61,7 +61,6 @@ struct pms { int depth; int input; s32 brightness, saturation, hue, contrast; - unsigned long in_use; struct mutex lock; int i2c_count; struct i2c_info i2cinfo[64]; @@ -931,25 +930,8 @@ static ssize_t pms_read(struct file *file, char __user *buf, return len; } -static int pms_exclusive_open(struct file *file) -{ - struct pms *dev = video_drvdata(file); - - return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0; -} - -static int pms_exclusive_release(struct file *file) -{ - struct pms *dev = video_drvdata(file); - - clear_bit(0, &dev->in_use); - return 0; -} - static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, - .open = pms_exclusive_open, - .release = pms_exclusive_release, .ioctl = video_ioctl2, .read = pms_read, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 712b300f723..301ef197d03 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2028,7 +2028,7 @@ static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, - video, s_fmt, &fmt); + vbi, s_sliced_fmt, &fmt.fmt.sliced); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index bf1e0fe9f4d..5ffa0d2b0b0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -49,7 +49,7 @@ struct pvr2_v4l2_dev { struct pvr2_v4l2_fh { struct pvr2_channel channel; - struct pvr2_v4l2_dev *dev_info; + struct pvr2_v4l2_dev *pdi; enum v4l2_priority prio; struct pvr2_ioread *rhp; struct file *file; @@ -162,7 +162,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_v4l2 *vp = fh->vhead; - struct pvr2_v4l2_dev *dev_info = fh->dev_info; + struct pvr2_v4l2_dev *pdi = fh->pdi; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; long ret = -EINVAL; @@ -183,7 +183,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_INPUT: case VIDIOC_S_TUNER: case VIDIOC_S_FREQUENCY: - ret = v4l2_prio_check(&vp->prio, &fh->prio); + ret = v4l2_prio_check(&vp->prio, fh->prio); if (ret) return ret; } @@ -564,14 +564,14 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_STREAMON: { - if (!fh->dev_info->stream) { + if (!fh->pdi->stream) { /* No stream defined for this node. This means that we're not currently allowed to stream from this node. */ ret = -EPERM; break; } - ret = pvr2_hdw_set_stream_type(hdw,dev_info->config); + ret = pvr2_hdw_set_stream_type(hdw,pdi->config); if (ret < 0) return ret; ret = pvr2_hdw_set_streaming(hdw,!0); break; @@ -579,7 +579,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_STREAMOFF: { - if (!fh->dev_info->stream) { + if (!fh->pdi->stream) { /* No stream defined for this node. This means that we're not currently allowed to stream from this node. */ @@ -972,7 +972,7 @@ static int pvr2_v4l2_release(struct file *file) fhp->rhp = NULL; } - v4l2_prio_close(&vp->prio, &fhp->prio); + v4l2_prio_close(&vp->prio, fhp->prio); file->private_data = NULL; if (fhp->vnext) { @@ -1032,7 +1032,7 @@ static int pvr2_v4l2_open(struct file *file) } init_waitqueue_head(&fhp->wait_data); - fhp->dev_info = dip; + fhp->pdi = dip; pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); pvr2_channel_init(&fhp->channel,vp->channel.mc_head); @@ -1093,7 +1093,7 @@ static int pvr2_v4l2_open(struct file *file) fhp->file = file; file->private_data = fhp; - v4l2_prio_open(&vp->prio,&fhp->prio); + v4l2_prio_open(&vp->prio, &fhp->prio); fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); @@ -1113,7 +1113,7 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) struct pvr2_hdw *hdw; if (fh->rhp) return 0; - if (!fh->dev_info->stream) { + if (!fh->pdi->stream) { /* No stream defined for this node. This means that we're not currently allowed to stream from this node. */ return -EPERM; @@ -1122,21 +1122,21 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) /* First read() attempt. Try to claim the stream and start it... */ if ((ret = pvr2_channel_claim_stream(&fh->channel, - fh->dev_info->stream)) != 0) { + fh->pdi->stream)) != 0) { /* Someone else must already have it */ return ret; } - fh->rhp = pvr2_channel_create_mpeg_stream(fh->dev_info->stream); + fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream); if (!fh->rhp) { pvr2_channel_claim_stream(&fh->channel,NULL); return -ENOMEM; } hdw = fh->channel.mc_head->hdw; - sp = fh->dev_info->stream->stream; + sp = fh->pdi->stream->stream; pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); - pvr2_hdw_set_stream_type(hdw,fh->dev_info->config); + pvr2_hdw_set_stream_type(hdw,fh->pdi->config); if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; return pvr2_ioread_set_enabled(fh->rhp,!0); } diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig index 340f954aba3..11980db22d3 100644 --- a/drivers/media/video/pwc/Kconfig +++ b/drivers/media/video/pwc/Kconfig @@ -39,7 +39,7 @@ config USB_PWC_DEBUG config USB_PWC_INPUT_EVDEV bool "USB Philips Cameras input events device support" default y - depends on USB_PWC=INPUT || INPUT=y + depends on USB_PWC && (USB_PWC=INPUT || INPUT=y) ---help--- This option makes USB Philips cameras register the snapshot button as an input device to report button events. diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 04bf5c11308..7fe70e71865 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -253,8 +253,8 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 9277194cd82..bbd9c11e2c5 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -555,15 +555,15 @@ static int rj54n1_commit(struct i2c_client *client) return ret; } -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, - u32 *out_w, u32 *out_h); +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h); static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { struct i2c_client *client = sd->priv; struct rj54n1 *rj54n1 = to_rj54n1(client); struct v4l2_rect *rect = &a->c; - unsigned int dummy = 0, output_w, output_h, + int dummy = 0, output_w, output_h, input_w = rect->width, input_h = rect->height; int ret; @@ -577,7 +577,7 @@ static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; - dev_dbg(&client->dev, "Scaling for %ux%u : %u = %ux%u\n", + dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", input_w, input_h, rj54n1->resize, output_w, output_h); ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); @@ -638,8 +638,8 @@ static int rj54n1_g_fmt(struct v4l2_subdev *sd, * the output one, updates the window sizes and returns an error or the resize * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. */ -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, - u32 *out_w, u32 *out_h) +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h) { struct i2c_client *client = sd->priv; struct rj54n1 *rj54n1 = to_rj54n1(client); @@ -749,7 +749,7 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, * improve the image quality or stability for larger frames (see comment * above), but I didn't check the framerate. */ - skip = min(resize / 1024, (unsigned)15); + skip = min(resize / 1024, 15U); inc_sel = 1 << skip; @@ -819,7 +819,7 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, *out_w = output_w; *out_h = output_h; - dev_dbg(&client->dev, "Scaled for %ux%u : %u = %ux%u, skip %u\n", + dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", *in_w, *in_h, resize, output_w, output_h, skip); return resize; @@ -1017,7 +1017,7 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct i2c_client *client = sd->priv; struct rj54n1 *rj54n1 = to_rj54n1(client); const struct rj54n1_datafmt *fmt; - unsigned int output_w, output_h, max_w, max_h, + int output_w, output_h, max_w, max_h, input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; int ret; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 3de914deb8e..3c7a79f3812 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1,7 +1,7 @@ /* * s2255drv.c - a driver for the Sensoray 2255 USB video capture device * - * Copyright (C) 2007-2008 by Sensoray Company Inc. + * Copyright (C) 2007-2010 by Sensoray Company Inc. * Dean Anderson * * Some video buffer code based on vivi driver: @@ -52,14 +52,19 @@ #include <linux/smp_lock.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <linux/vmalloc.h> #include <linux/usb.h> +#define S2255_MAJOR_VERSION 1 +#define S2255_MINOR_VERSION 20 +#define S2255_RELEASE 0 +#define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \ + S2255_MINOR_VERSION, \ + S2255_RELEASE) #define FIRMWARE_FILE_NAME "f2255usb.bin" - - /* default JPEG quality */ #define S2255_DEF_JPEG_QUAL 50 /* vendor request in */ @@ -76,14 +81,14 @@ #define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) #define S2255_DEF_BUFS 16 #define S2255_SETMODE_TIMEOUT 500 -#define MAX_CHANNELS 4 -#define S2255_MARKER_FRAME 0x2255DA4AL -#define S2255_MARKER_RESPONSE 0x2255ACACL -#define S2255_RESPONSE_SETMODE 0x01 -#define S2255_RESPONSE_FW 0x10 +#define S2255_VIDSTATUS_TIMEOUT 350 +#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) +#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) +#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) +#define S2255_RESPONSE_FW cpu_to_le32(0x10) +#define S2255_RESPONSE_STATUS cpu_to_le32(0x20) #define S2255_USB_XFER_SIZE (16 * 1024) #define MAX_CHANNELS 4 -#define MAX_PIPE_BUFFERS 1 #define SYS_FRAMES 4 /* maximum size is PAL full size plus room for the marker header(s) */ #define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) @@ -118,9 +123,10 @@ #define COLOR_YUVPK 2 /* YUV packed */ #define COLOR_Y8 4 /* monochrome */ #define COLOR_JPG 5 /* JPEG */ -#define MASK_COLOR 0xff -#define MASK_JPG_QUALITY 0xff00 +#define MASK_COLOR 0x000000ff +#define MASK_JPG_QUALITY 0x0000ff00 +#define MASK_INPUT_TYPE 0x000f0000 /* frame decimation. Not implemented by V4L yet(experimental in V4L) */ #define FDEC_1 1 /* capture every frame. default */ #define FDEC_2 2 /* capture every 2nd frame */ @@ -139,12 +145,12 @@ #define DEF_HUE 0 /* usb config commands */ -#define IN_DATA_TOKEN 0x2255c0de -#define CMD_2255 0xc2255000 -#define CMD_SET_MODE (CMD_2255 | 0x10) -#define CMD_START (CMD_2255 | 0x20) -#define CMD_STOP (CMD_2255 | 0x30) -#define CMD_STATUS (CMD_2255 | 0x40) +#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) +#define CMD_2255 cpu_to_le32(0xc2255000) +#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) +#define CMD_START cpu_to_le32((CMD_2255 | 0x20)) +#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) +#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) struct s2255_mode { u32 format; /* input video format (NTSC, PAL) */ @@ -194,7 +200,6 @@ struct s2255_dmaqueue { #define S2255_FW_SUCCESS 2 #define S2255_FW_FAILED 3 #define S2255_FW_DISCONNECTING 4 - #define S2255_FW_MARKER cpu_to_le32(0x22552f2f) /* 2255 read states */ #define S2255_READ_IDLE 0 @@ -223,8 +228,10 @@ struct s2255_pipeinfo { struct s2255_fmt; /*forward declaration */ struct s2255_dev { + struct video_device vdev[MAX_CHANNELS]; + struct v4l2_device v4l2_dev; + atomic_t channels; /* number of channels registered */ int frames; - int users[MAX_CHANNELS]; struct mutex lock; struct mutex open_lock; int resources[MAX_CHANNELS]; @@ -233,11 +240,10 @@ struct s2255_dev { u8 read_endpoint; struct s2255_dmaqueue vidq[MAX_CHANNELS]; - struct video_device *vdev[MAX_CHANNELS]; struct timer_list timer; struct s2255_fw *fw_data; - struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS]; - struct s2255_bufferi buffer[MAX_CHANNELS]; + struct s2255_pipeinfo pipe; + struct s2255_bufferi buffer[MAX_CHANNELS]; struct s2255_mode mode[MAX_CHANNELS]; /* jpeg compression */ struct v4l2_jpegcompression jc[MAX_CHANNELS]; @@ -261,11 +267,21 @@ struct s2255_dev { 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; - struct kref kref; spinlock_t slock; + /* dsp firmware version (f2255usb.bin) */ + int dsp_fw_ver; + u16 pid; /* product id */ }; -#define to_s2255_dev(d) container_of(d, struct s2255_dev, kref) + +static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); +} struct s2255_fmt { char *name; @@ -296,17 +312,43 @@ struct s2255_fh { /* current cypress EEPROM firmware version */ #define S2255_CUR_USB_FWVER ((3 << 8) | 6) -#define S2255_MAJOR_VERSION 1 -#define S2255_MINOR_VERSION 14 -#define S2255_RELEASE 0 -#define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \ - S2255_MINOR_VERSION, \ - S2255_RELEASE) - -/* vendor ids */ -#define USB_S2255_VENDOR_ID 0x1943 -#define USB_S2255_PRODUCT_ID 0x2255 +/* current DSP FW version */ +#define S2255_CUR_DSP_FWVER 8 +/* Need DSP version 5+ for video status feature */ +#define S2255_MIN_DSP_STATUS 5 +#define S2255_MIN_DSP_COLORFILTER 8 #define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) + +/* private V4L2 controls */ + +/* + * The following chart displays how COLORFILTER should be set + * ========================================================= + * = fourcc = COLORFILTER = + * = =============================== + * = = 0 = 1 = + * ========================================================= + * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= + * = = s-video or = composite = + * = = B/W camera = input = + * ========================================================= + * = other = color, svideo = color, = + * = = = composite = + * ========================================================= + * + * Notes: + * channels 0-3 on 2255 are composite + * channels 0-1 on 2257 are composite, 2-3 are s-video + * If COLORFILTER is 0 with a composite color camera connected, + * the output will appear monochrome but hatching + * will occur. + * COLORFILTER is different from "color killer" and "color effects" + * for reasons above. + */ +#define S2255_V4L2_YC_ON 1 +#define S2255_V4L2_YC_OFF 0 +#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) + /* frame prefix size (sent once every frame) */ #define PREFIX_SIZE 512 @@ -325,9 +367,8 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, struct s2255_mode *mode); static int s2255_board_shutdown(struct s2255_dev *dev); -static void s2255_exit_v4l(struct s2255_dev *dev); static void s2255_fwload_start(struct s2255_dev *dev, int reset); -static void s2255_destroy(struct kref *kref); +static void s2255_destroy(struct s2255_dev *dev); static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, u16 index, u16 value, void *buf, s32 buf_len, int bOut); @@ -347,7 +388,6 @@ static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, static struct usb_driver s2255_driver; - /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ @@ -362,58 +402,16 @@ module_param(video_nr, int, 0644); MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); /* USB device table */ +#define USB_SENSORAY_VID 0x1943 static struct usb_device_id s2255_table[] = { - {USB_DEVICE(USB_S2255_VENDOR_ID, USB_S2255_PRODUCT_ID)}, + {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, + {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, s2255_table); - #define BUFFER_TIMEOUT msecs_to_jiffies(400) -/* supported controls */ -static struct v4l2_queryctrl s2255_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = -127, - .maximum = 128, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = DEF_CONTRAST, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = DEF_SATURATION, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = DEF_HUE, - .flags = 0, - } -}; - -static int qctl_regs[ARRAY_SIZE(s2255_qctrl)]; - /* image formats. */ static const struct s2255_fmt formats[] = { { @@ -505,7 +503,7 @@ static void s2255_reset_dsppower(struct s2255_dev *dev) static void s2255_timer(unsigned long user_data) { struct s2255_fw *data = (struct s2255_fw *)user_data; - dprintk(100, "s2255 timer\n"); + dprintk(100, "%s\n", __func__); if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { printk(KERN_ERR "s2255: can't submit urb\n"); atomic_set(&data->fw_state, S2255_FW_FAILED); @@ -527,7 +525,7 @@ static void s2255_fwchunk_complete(struct urb *urb) struct s2255_fw *data = urb->context; struct usb_device *udev = urb->dev; int len; - dprintk(100, "udev %p urb %p", udev, urb); + dprintk(100, "%s: udev %p urb %p", __func__, udev, urb); if (urb->status) { dev_err(&udev->dev, "URB failed with status %d\n", urb->status); atomic_set(&data->fw_state, S2255_FW_FAILED); @@ -573,8 +571,8 @@ static void s2255_fwchunk_complete(struct urb *urb) data->fw_loaded += len; } else { atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); + dprintk(100, "%s: firmware upload complete\n", __func__); } - dprintk(100, "2255 complete done\n"); return; } @@ -585,9 +583,7 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) struct s2255_buffer *buf; unsigned long flags = 0; int rc = 0; - dprintk(2, "wakeup: %p channel: %d\n", &dma_q, chn); spin_lock_irqsave(&dev->slock, flags); - if (list_empty(&dma_q->active)) { dprintk(1, "No active queue to serve\n"); rc = -1; @@ -595,23 +591,19 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) } buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); - list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); - dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i); s2255_fillbuff(dev, buf, dma_q->channel, jpgsize); wake_up(&buf->vb.done); - dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); + dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); unlock: spin_unlock_irqrestore(&dev->slock, flags); return 0; } - static const struct s2255_fmt *format_by_fourcc(int fourcc) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(formats); i++) { if (-1 == formats[i].fourcc) continue; @@ -621,9 +613,6 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) return NULL; } - - - /* video buffer vmalloc implementation based partly on VIVI driver which is * Copyright (c) 2006 by * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> @@ -703,8 +692,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, if (0 == *count) *count = S2255_DEF_BUFS; - while (*size * (*count) > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } @@ -727,10 +716,10 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (fh->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 ((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]))) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } @@ -747,7 +736,6 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, buf->vb.height = fh->height; buf->vb.field = field; - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); if (rc < 0) @@ -767,9 +755,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) struct s2255_fh *fh = vq->priv_data; struct s2255_dev *dev = fh->dev; struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel]; - dprintk(1, "%s\n", __func__); - buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active); } @@ -828,6 +814,27 @@ static void res_free(struct s2255_dev *dev, struct s2255_fh *fh) dprintk(1, "res: put\n"); } +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + static const char *colorfilter[] = { + "Off", + "On", + NULL + }; + if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { + int i; + const char **menu_items = colorfilter; + for (i = 0; i < qmenu->index && menu_items[i]; i++) + ; /* do nothing (from v4l2-common.c) */ + if (menu_items[i] == NULL || menu_items[i][0] == '\0') + return -EINVAL; + strlcpy(qmenu->name, menu_items[qmenu->index], + sizeof(qmenu->name)); + return 0; + } + return v4l2_ctrl_query_menu(qmenu, NULL, NULL); +} static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) @@ -883,7 +890,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, int is_ntsc; is_ntsc = - (dev->vdev[fh->channel]->current_norm & V4L2_STD_NTSC) ? 1 : 0; + (dev->vdev[fh->channel].current_norm & V4L2_STD_NTSC) ? 1 : 0; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -894,10 +901,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, if (field == V4L2_FIELD_ANY) b_any_field = 1; - dprintk(4, "try format %d \n", is_ntsc); - /* supports 3 sizes. see s2255drv.h */ - dprintk(50, "width test %d, height %d\n", - f->fmt.pix.width, f->fmt.pix.height); + dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", + __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); if (is_ntsc) { /* NTSC */ if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { @@ -952,29 +957,24 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } } if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { - dprintk(50, "pal 704\n"); f->fmt.pix.width = LINE_SZ_4CIFS_PAL; field = V4L2_FIELD_SEQ_TB; } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { - dprintk(50, "pal 352A\n"); f->fmt.pix.width = LINE_SZ_2CIFS_PAL; field = V4L2_FIELD_TOP; } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { - dprintk(50, "pal 352B\n"); f->fmt.pix.width = LINE_SZ_1CIFS_PAL; field = V4L2_FIELD_TOP; } else { - dprintk(50, "pal 352C\n"); f->fmt.pix.width = LINE_SZ_1CIFS_PAL; field = V4L2_FIELD_TOP; } } - - dprintk(50, "width %d height %d field %d \n", f->fmt.pix.width, - f->fmt.pix.height, f->fmt.pix.field); f->fmt.pix.field = field; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + dprintk(50, "%s: set width %d height %d field %d\n", __func__, + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); return 0; } @@ -1006,7 +1006,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } if (res_locked(fh->dev, fh)) { - dprintk(1, "can't change format after started\n"); + dprintk(1, "%s: channel busy\n", __func__); ret = -EBUSY; goto out_s_fmt; } @@ -1016,17 +1016,14 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->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])) { + 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 & - V4L2_MODE_HIGHQUALITY) { + V4L2_MODE_HIGHQUALITY) fh->mode.scale = SCALE_4CIFSI; - dprintk(2, "scale 4CIFSI\n"); - } else { + else fh->mode.scale = SCALE_4CIFS; - dprintk(2, "scale 4CIFS\n"); - } } else fh->mode.scale = SCALE_2CIFS; @@ -1037,19 +1034,23 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, /* color mode */ switch (fh->fmt->fourcc) { case V4L2_PIX_FMT_GREY: - fh->mode.color = COLOR_Y8; + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_Y8; break; case V4L2_PIX_FMT_JPEG: - fh->mode.color = COLOR_JPG | - (fh->dev->jc[fh->channel].quality << 8); + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_JPG; + fh->mode.color |= (fh->dev->jc[fh->channel].quality << 8); break; case V4L2_PIX_FMT_YUV422P: - fh->mode.color = COLOR_YUVPL; + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_YUVPL; break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: default: - fh->mode.color = COLOR_YUVPK; + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_YUVPK; break; } ret = 0; @@ -1178,19 +1179,13 @@ static u32 get_transfer_size(struct s2255_mode *mode) return usbInSize; } -static void dump_verify_mode(struct s2255_dev *sdev, struct s2255_mode *mode) +static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) { struct device *dev = &sdev->udev->dev; dev_info(dev, "------------------------------------------------\n"); - dev_info(dev, "verify mode\n"); - dev_info(dev, "format: %d\n", mode->format); - dev_info(dev, "scale: %d\n", mode->scale); - dev_info(dev, "fdec: %d\n", mode->fdec); - dev_info(dev, "color: %d\n", mode->color); + dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); + dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); dev_info(dev, "bright: 0x%x\n", mode->bright); - dev_info(dev, "restart: 0x%x\n", mode->restart); - dev_info(dev, "usb_block: 0x%x\n", mode->usb_block); - dev_info(dev, "single: 0x%x\n", mode->single); dev_info(dev, "------------------------------------------------\n"); } @@ -1206,44 +1201,38 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, struct s2255_mode *mode) { int res; - u32 *buffer; + __le32 *buffer; unsigned long chn_rev; - mutex_lock(&dev->lock); chn_rev = G_chnmap[chn]; - dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale); - dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn], - dev->mode[chn].scale); - dprintk(2, "mode contrast %x\n", mode->contrast); - + dprintk(3, "%s channel %lu\n", __func__, chn); /* if JPEG, set the quality */ - if ((mode->color & MASK_COLOR) == COLOR_JPG) - mode->color = (dev->jc[chn].quality << 8) | COLOR_JPG; - + 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); + } /* save the mode */ dev->mode[chn] = *mode; dev->req_image_size[chn] = get_transfer_size(mode); - dprintk(1, "transfer size %ld\n", dev->req_image_size[chn]); - + dprintk(1, "%s: reqsize %ld\n", __func__, dev->req_image_size[chn]); buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); mutex_unlock(&dev->lock); return -ENOMEM; } - /* set the mode */ buffer[0] = IN_DATA_TOKEN; - buffer[1] = (u32) chn_rev; + 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; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (debug) - dump_verify_mode(dev, mode); + s2255_print_cfg(dev, mode); kfree(buffer); - dprintk(1, "set mode done chn %lu, %d\n", chn, res); - /* wait at least 3 frames before continuing */ if (mode->restart) { wait_event_timeout(dev->wait_setmode[chn], @@ -1254,10 +1243,46 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, res = -EFAULT; } } - /* clear the restart flag */ dev->mode[chn].restart = 0; mutex_unlock(&dev->lock); + dprintk(1, "%s chn %lu, result: %d\n", __func__, chn, res); + return res; +} + +static int s2255_cmd_status(struct s2255_dev *dev, unsigned long chn, + u32 *pstatus) +{ + int res; + __le32 *buffer; + u32 chn_rev; + mutex_lock(&dev->lock); + chn_rev = G_chnmap[chn]; + dprintk(4, "%s chan %lu\n", __func__, chn); + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + /* form the get vid status command */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[2] = CMD_STATUS; + *pstatus = 0; + dev->vidstatus_ready[chn] = 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), + msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); + if (dev->vidstatus_ready[chn] != 1) { + printk(KERN_DEBUG "s2255: no vidstatus response\n"); + res = -EFAULT; + } + *pstatus = dev->vidstatus[chn]; + dprintk(4, "%s, vid status %d\n", __func__, *pstatus); + mutex_unlock(&dev->lock); return res; } @@ -1291,7 +1316,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) new_mode = &fh->mode; old_mode = &fh->dev->mode[chn]; - if (new_mode->color != old_mode->color) + 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; @@ -1302,7 +1327,6 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) new_mode->restart = 0; *old_mode = *new_mode; dev->cur_fmt[chn] = fh->fmt; - dprintk(1, "%s[%d]\n", __func__, chn); dev->last_frame[chn] = -1; dev->bad_payload[chn] = 0; dev->cur_frame[chn] = 0; @@ -1325,7 +1349,6 @@ 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); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_ERR "invalid fh type0\n"); @@ -1347,27 +1370,32 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) struct s2255_mode *mode; struct videobuf_queue *q = &fh->vb_vidq; int ret = 0; - mutex_lock(&q->vb_lock); if (videobuf_queue_is_busy(q)) { dprintk(1, "queue busy\n"); ret = -EBUSY; goto out_s_std; } - if (res_locked(fh->dev, fh)) { dprintk(1, "can't change standard after started\n"); ret = -EBUSY; goto out_s_std; } mode = &fh->mode; - if (*i & V4L2_STD_NTSC) { - dprintk(4, "vidioc_s_std NTSC\n"); - mode->format = FORMAT_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; + } } else if (*i & V4L2_STD_PAL) { - dprintk(4, "vidioc_s_std PAL\n"); + dprintk(4, "%s PAL\n", __func__); mode->format = FORMAT_PAL; + if (mode->format != FORMAT_PAL) { + mode->format = FORMAT_PAL; + mode->fdec = FDEC_1; + } } else { ret = -EINVAL; } @@ -1386,12 +1414,32 @@ out_s_std: static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + u32 status = 0; if (inp->index != 0) return -EINVAL; - inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = S2255_NORMS; - strlcpy(inp->name, "Camera", sizeof(inp->name)); + inp->status = 0; + if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { + int rc; + rc = s2255_cmd_status(dev, fh->channel, &status); + dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); + if (rc == 0) + inp->status = (status & 0x01) ? 0 + : V4L2_IN_ST_NO_SIGNAL; + } + switch (dev->pid) { + case 0x2255: + default: + strlcpy(inp->name, "Composite", sizeof(inp->name)); + break; + case 0x2257: + strlcpy(inp->name, (fh->channel < 2) ? "Composite" : "S-Video", + sizeof(inp->name)); + break; + } return 0; } @@ -1411,74 +1459,113 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { - int i; - - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) - if (qc->id && qc->id == s2255_qctrl[i].id) { - memcpy(qc, &(s2255_qctrl[i]), sizeof(*qc)); - return 0; - } - - dprintk(4, "query_ctrl -EINVAL %d\n", qc->id); - return -EINVAL; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); + break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); + break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); + break; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_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)) + return -EINVAL; + strlcpy(qc->name, "Color Filter", sizeof(qc->name)); + qc->type = V4L2_CTRL_TYPE_MENU; + qc->minimum = 0; + qc->maximum = 1; + qc->step = 1; + qc->default_value = 1; + qc->flags = 0; + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d\n", __func__, qc->id); + return 0; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - int i; - - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) - if (ctrl->id == s2255_qctrl[i].id) { - ctrl->value = qctl_regs[i]; - return 0; - } - dprintk(4, "g_ctrl -EINVAL\n"); - - return -EINVAL; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = fh->mode.bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = fh->mode.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = fh->mode.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = fh->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)) + return -EINVAL; + ctrl->value = !((fh->mode.color & MASK_INPUT_TYPE) >> 16); + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); + return 0; } static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - int i; struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; struct s2255_mode *mode; mode = &fh->mode; - dprintk(4, "vidioc_s_ctrl\n"); - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) { - if (ctrl->id == s2255_qctrl[i].id) { - if (ctrl->value < s2255_qctrl[i].minimum || - ctrl->value > s2255_qctrl[i].maximum) - return -ERANGE; - - qctl_regs[i] = ctrl->value; - /* update the mode to the corresponding value */ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - mode->bright = ctrl->value; - break; - case V4L2_CID_CONTRAST: - mode->contrast = ctrl->value; - break; - case V4L2_CID_HUE: - mode->hue = ctrl->value; - break; - case V4L2_CID_SATURATION: - mode->saturation = ctrl->value; - break; - } - 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); - return 0; - } + dprintk(4, "%s\n", __func__); + /* update the mode to the corresponding value */ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + mode->bright = ctrl->value; + break; + case V4L2_CID_CONTRAST: + mode->contrast = ctrl->value; + break; + case V4L2_CID_HUE: + mode->hue = ctrl->value; + break; + case V4L2_CID_SATURATION: + 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)) + return -EINVAL; + mode->color &= ~MASK_INPUT_TYPE; + mode->color |= ((ctrl->value ? 0 : 1) << 16); + break; + default: + return -EINVAL; } - return -EINVAL; + 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); + return 0; } static int vidioc_g_jpegcomp(struct file *file, void *priv, @@ -1487,7 +1574,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; *jc = dev->jc[fh->channel]; - dprintk(2, "getting jpegcompression, quality %d\n", jc->quality); + dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1499,7 +1586,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, if (jc->quality < 0 || jc->quality > 100) return -EINVAL; dev->jc[fh->channel].quality = jc->quality; - dprintk(2, "setting jpeg quality %d\n", jc->quality); + dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1508,10 +1595,34 @@ static int vidioc_g_parm(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; + __u32 def_num, def_dem; 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; - dprintk(2, "getting parm %d\n", sp->parm.capture.capturemode); + def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + sp->parm.capture.timeperframe.denominator = def_dem; + switch (fh->mode.fdec) { + default: + case FDEC_1: + sp->parm.capture.timeperframe.numerator = def_num; + break; + case FDEC_2: + sp->parm.capture.timeperframe.numerator = def_num * 2; + break; + case FDEC_3: + sp->parm.capture.timeperframe.numerator = def_num * 3; + break; + case FDEC_5: + sp->parm.capture.timeperframe.numerator = def_num * 5; + break; + } + dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator); return 0; } @@ -1520,15 +1631,79 @@ static int vidioc_s_parm(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; - + int fdec = FDEC_1; + __u32 def_num, def_dem; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + /* high quality capture mode requires a stream restart */ + if (dev->cap_parm[fh->channel].capturemode + != sp->parm.capture.capturemode && res_locked(fh->dev, fh)) + return -EBUSY; + def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (fh->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) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { + sp->parm.capture.timeperframe.numerator = def_num * 2; + fdec = FDEC_2; + } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { + sp->parm.capture.timeperframe.numerator = def_num * 3; + fdec = FDEC_3; + } else { + sp->parm.capture.timeperframe.numerator = def_num * 5; + fdec = FDEC_5; + } + fh->mode.fdec = fdec; + sp->parm.capture.timeperframe.denominator = def_dem; + s2255_set_mode(dev, fh->channel, &fh->mode); + dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", + __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator, fdec); + return 0; +} - dev->cap_parm[fh->channel].capturemode = sp->parm.capture.capturemode; - dprintk(2, "setting param capture mode %d\n", - sp->parm.capture.capturemode); +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fe) +{ + int is_ntsc = 0; +#define NUM_FRAME_ENUMS 4 + int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; + if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS) + return -EINVAL; + switch (fe->width) { + case 640: + if (fe->height != 240 && fe->height != 480) + return -EINVAL; + is_ntsc = 1; + break; + case 320: + if (fe->height != 240) + return -EINVAL; + is_ntsc = 1; + break; + case 704: + if (fe->height != 288 && fe->height != 576) + return -EINVAL; + break; + case 352: + if (fe->height != 288) + return -EINVAL; + break; + default: + return -EINVAL; + } + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fe->discrete.denominator = is_ntsc ? 30000 : 25000; + fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; + dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator, + fe->discrete.denominator); return 0; } + static int s2255_open(struct file *file) { struct video_device *vdev = video_devdata(file); @@ -1538,31 +1713,29 @@ static int s2255_open(struct file *file) int i = 0; int cur_channel = -1; int state; - dprintk(1, "s2255: open called (dev=%s)\n", video_device_node_name(vdev)); - lock_kernel(); - for (i = 0; i < MAX_CHANNELS; i++) { - if (dev->vdev[i] == vdev) { + if (&dev->vdev[i] == vdev) { cur_channel = i; break; } } - - if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING) { - unlock_kernel(); - printk(KERN_INFO "disconnecting\n"); + if (i == MAX_CHANNELS) return -ENODEV; - } - kref_get(&dev->kref); - mutex_lock(&dev->open_lock); - - dev->users[cur_channel]++; - dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]); - switch (atomic_read(&dev->fw_data->fw_state)) { + /* + * open lock necessary to prevent multiple instances + * of v4l-conf (or other programs) from simultaneously + * reloading firmware. + */ + mutex_lock(&dev->open_lock); + state = atomic_read(&dev->fw_data->fw_state); + switch (state) { + case S2255_FW_DISCONNECTING: + mutex_unlock(&dev->open_lock); + return -ENODEV; case S2255_FW_FAILED: s2255_dev_err(&dev->udev->dev, "firmware load failed. retrying.\n"); @@ -1573,6 +1746,8 @@ static int s2255_open(struct file *file) (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING)), msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); break; case S2255_FW_NOTLOADED: case S2255_FW_LOADED_DSPWAIT: @@ -1584,53 +1759,50 @@ static int s2255_open(struct file *file) == S2255_FW_SUCCESS) || (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING)), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); break; case S2255_FW_SUCCESS: default: break; } - state = atomic_read(&dev->fw_data->fw_state); - if (state != S2255_FW_SUCCESS) { - int rc; - switch (state) { - case S2255_FW_FAILED: - printk(KERN_INFO "2255 FW load failed. %d\n", state); - rc = -ENODEV; - break; - case S2255_FW_DISCONNECTING: - printk(KERN_INFO "%s: disconnecting\n", __func__); - rc = -ENODEV; - break; - case S2255_FW_LOADED_DSPWAIT: - case S2255_FW_NOTLOADED: - printk(KERN_INFO "%s: firmware not loaded yet" - "please try again later\n", - __func__); - rc = -EAGAIN; - break; - default: - printk(KERN_INFO "%s: unknown state\n", __func__); - rc = -EFAULT; - break; - } - dev->users[cur_channel]--; + /* state may have changed in above switch statement */ + switch (state) { + case S2255_FW_SUCCESS: + break; + case S2255_FW_FAILED: + printk(KERN_INFO "2255 firmware load failed.\n"); + mutex_unlock(&dev->open_lock); + return -ENODEV; + case S2255_FW_DISCONNECTING: + printk(KERN_INFO "%s: disconnecting\n", __func__); mutex_unlock(&dev->open_lock); - kref_put(&dev->kref, s2255_destroy); - unlock_kernel(); - return rc; + return -ENODEV; + case S2255_FW_LOADED_DSPWAIT: + case S2255_FW_NOTLOADED: + printk(KERN_INFO "%s: firmware not loaded yet" + "please try again later\n", + __func__); + /* + * Timeout on firmware load means device unusable. + * Set firmware failure state. + * On next s2255_open the firmware will be reloaded. + */ + atomic_set(&dev->fw_data->fw_state, + S2255_FW_FAILED); + mutex_unlock(&dev->open_lock); + return -EAGAIN; + default: + printk(KERN_INFO "%s: unknown state\n", __func__); + mutex_unlock(&dev->open_lock); + return -EFAULT; } - + mutex_unlock(&dev->open_lock); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - kref_put(&dev->kref, s2255_destroy); - unlock_kernel(); + if (NULL == fh) return -ENOMEM; - } - file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1640,35 +1812,23 @@ static int s2255_open(struct file *file) 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; } - - - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) - qctl_regs[i] = s2255_qctrl[i].default_value; - - dprintk(1, "s2255drv: open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[type], - dev->users[cur_channel]); - dprintk(2, "s2255drv: open: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", + 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]); - dprintk(4, "s2255drv: open: list_empty active=%d\n", + dprintk(4, "%s: list_empty active=%d\n", __func__, list_empty(&dev->vidq[cur_channel].active)); - videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct s2255_buffer), fh); - - mutex_unlock(&dev->open_lock); - unlock_kernel(); return 0; } @@ -1679,39 +1839,19 @@ static unsigned int s2255_poll(struct file *file, struct s2255_fh *fh = file->private_data; int rc; dprintk(100, "%s\n", __func__); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; - rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); return rc; } -static void s2255_destroy(struct kref *kref) +static void s2255_destroy(struct s2255_dev *dev) { - struct s2255_dev *dev = to_s2255_dev(kref); - int i; - if (!dev) { - printk(KERN_ERR "s2255drv: kref problem\n"); - return; - } - 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]); - } - mutex_lock(&dev->open_lock); - /* reset the DSP so firmware can be reload next time */ - s2255_reset_dsppower(dev); - s2255_exit_v4l(dev); /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); /* make sure firmware still not trying to load */ del_timer(&dev->timer); /* only started in .probe and .open */ - if (dev->fw_data->fw_urb) { - dprintk(2, "kill fw_urb\n"); usb_kill_urb(dev->fw_data->fw_urb); usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; @@ -1720,24 +1860,22 @@ static void s2255_destroy(struct kref *kref) release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); + /* reset the DSP so firmware can be reloaded next time */ + s2255_reset_dsppower(dev); + mutex_destroy(&dev->open_lock); + mutex_destroy(&dev->lock); usb_put_dev(dev->udev); dprintk(1, "%s", __func__); - - mutex_unlock(&dev->open_lock); kfree(dev); } -static int s2255_close(struct file *file) +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); - if (!dev) return -ENODEV; - - mutex_lock(&dev->open_lock); - /* turn off stream */ if (res_check(fh)) { if (dev->b_acquire[fh->channel]) @@ -1745,15 +1883,8 @@ static int s2255_close(struct file *file) videobuf_streamoff(&fh->vb_vidq); res_free(dev, fh); } - videobuf_mmap_free(&fh->vb_vidq); - dev->users[fh->channel]--; - - mutex_unlock(&dev->open_lock); - - kref_put(&dev->kref, s2255_destroy); - dprintk(1, "s2255: close called (dev=%s, users=%d)\n", - video_device_node_name(vdev), dev->users[fh->channel]); + dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); kfree(fh); return 0; } @@ -1765,27 +1896,25 @@ static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) if (!fh) return -ENODEV; - dprintk(4, "mmap called, vma=0x%08lx\n", (unsigned long)vma); - + dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); - - dprintk(4, "vma start=0x%08lx, size=%ld, ret=%d\n", + dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__, (unsigned long)vma->vm_start, (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; } static const struct v4l2_file_operations s2255_fops_v4l = { .owner = THIS_MODULE, .open = s2255_open, - .release = s2255_close, + .release = s2255_release, .poll = s2255_poll, .ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = s2255_mmap_v4l, }; static const struct v4l2_ioctl_ops s2255_ioctl_ops = { + .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1811,13 +1940,23 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { .vidioc_g_jpegcomp = vidioc_g_jpegcomp, .vidioc_s_parm = vidioc_s_parm, .vidioc_g_parm = vidioc_g_parm, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, }; +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)) + s2255_destroy(dev); + return; +} + static struct video_device template = { .name = "s2255v", .fops = &s2255_fops_v4l, .ioctl_ops = &s2255_ioctl_ops, - .release = video_device_release, + .release = s2255_video_device_release, .tvnorms = S2255_NORMS, .current_norm = V4L2_STD_NTSC_M, }; @@ -1827,7 +1966,9 @@ static int s2255_probe_v4l(struct s2255_dev *dev) int ret; int i; int cur_nr = video_nr; - + 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++) { @@ -1835,45 +1976,39 @@ static int s2255_probe_v4l(struct s2255_dev *dev) dev->vidq[i].dev = dev; dev->vidq[i].channel = i; /* register 4 video devices */ - dev->vdev[i] = video_device_alloc(); - memcpy(dev->vdev[i], &template, sizeof(struct video_device)); - dev->vdev[i]->parent = &dev->interface->dev; - video_set_drvdata(dev->vdev[i], dev); + memcpy(&dev->vdev[i], &template, sizeof(struct video_device)); + dev->vdev[i].v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(&dev->vdev[i], dev); if (video_nr == -1) - ret = video_register_device(dev->vdev[i], + ret = video_register_device(&dev->vdev[i], VFL_TYPE_GRABBER, video_nr); else - ret = video_register_device(dev->vdev[i], + ret = video_register_device(&dev->vdev[i], VFL_TYPE_GRABBER, cur_nr + i); - video_set_drvdata(dev->vdev[i], dev); - - if (ret != 0) { + if (ret) { dev_err(&dev->udev->dev, "failed to register video device!\n"); - return ret; + break; } + atomic_inc(&dev->channels); + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(&dev->vdev[i])); + } + printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n", S2255_MAJOR_VERSION, S2255_MINOR_VERSION); - return ret; -} - -static void s2255_exit_v4l(struct s2255_dev *dev) -{ - - int i; - for (i = 0; i < MAX_CHANNELS; i++) { - if (video_is_registered(dev->vdev[i])) { - video_unregister_device(dev->vdev[i]); - printk(KERN_INFO "s2255 unregistered\n"); - } else { - video_device_release(dev->vdev[i]); - printk(KERN_INFO "s2255 released\n"); - } + /* if no channels registered, return error and probe will fail*/ + if (atomic_read(&dev->channels) == 0) { + v4l2_device_unregister(&dev->v4l2_dev); + return ret; } + if (atomic_read(&dev->channels) != MAX_CHANNELS) + printk(KERN_WARNING "s2255: Not all channels available.\n"); + return 0; } /* this function moves the usb stream read pipe data @@ -1907,14 +2042,14 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (frm->ulState == S2255_READ_IDLE) { int jj; unsigned int cc; - s32 *pdword; + __le32 *pdword; /*data from dsp is little endian */ int payload; /* search for marker codes */ pdata = (unsigned char *)pipe_info->transfer_buffer; + pdword = (__le32 *)pdata; for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { - switch (*(s32 *) pdata) { + switch (*pdword) { case S2255_MARKER_FRAME: - pdword = (s32 *)pdata; dprintk(4, "found frame marker at offset:" " %d [%x %x]\n", jj, pdata[0], pdata[1]); @@ -1938,7 +2073,6 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) dev->jpg_size[dev->cc] = pdword[4]; break; case S2255_MARKER_RESPONSE: - pdword = (s32 *)pdata; pdata += DEF_USB_BLOCK; jj += DEF_USB_BLOCK; if (pdword[1] >= MAX_CHANNELS) @@ -1955,7 +2089,6 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) dprintk(5, "setmode ready %d\n", cc); break; case S2255_RESPONSE_FW: - dev->chn_ready |= (1 << cc); if ((dev->chn_ready & 0x0f) != 0x0f) break; @@ -1965,6 +2098,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) S2255_FW_SUCCESS); 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]); + dprintk(5, "got vidstatus %x chan %d\n", + pdword[3], cc); + break; default: printk(KERN_INFO "s2255 unknown resp\n"); } @@ -2165,28 +2305,22 @@ static int s2255_release_sys_buffers(struct s2255_dev *dev, static int s2255_board_init(struct s2255_dev *dev) { - int j; struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; int fw_ver; + int j; + struct s2255_pipeinfo *pipe = &dev->pipe; dprintk(4, "board init: %p", dev); - - for (j = 0; j < MAX_PIPE_BUFFERS; j++) { - struct s2255_pipeinfo *pipe = &dev->pipes[j]; - - memset(pipe, 0, sizeof(*pipe)); - pipe->dev = dev; - pipe->cur_transfer_size = S2255_USB_XFER_SIZE; - pipe->max_transfer_size = S2255_USB_XFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, - GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { - dprintk(1, "out of memory!\n"); - return -ENOMEM; - } - + memset(pipe, 0, sizeof(*pipe)); + pipe->dev = dev; + pipe->cur_transfer_size = S2255_USB_XFER_SIZE; + pipe->max_transfer_size = S2255_USB_XFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + dprintk(1, "out of memory!\n"); + return -ENOMEM; } - /* query the firmware */ fw_ver = s2255_get_fx2fw(dev); @@ -2203,6 +2337,8 @@ static int s2255_board_init(struct s2255_dev *dev) for (j = 0; j < MAX_CHANNELS; j++) { dev->b_acquire[j] = 0; dev->mode[j] = 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; @@ -2213,16 +2349,14 @@ static int s2255_board_init(struct s2255_dev *dev) } /* start read pipe */ s2255_start_readpipe(dev); - - dprintk(1, "S2255: board initialized\n"); + dprintk(1, "%s: success\n", __func__); return 0; } static int s2255_board_shutdown(struct s2255_dev *dev) { u32 i; - - dprintk(1, "S2255: board shutdown: %p", dev); + dprintk(1, "%s: dev: %p", __func__, dev); for (i = 0; i < MAX_CHANNELS; i++) { if (dev->b_acquire[i]) @@ -2233,12 +2367,8 @@ static int s2255_board_shutdown(struct s2255_dev *dev) for (i = 0; i < MAX_CHANNELS; i++) s2255_release_sys_buffers(dev, i); - - /* release transfer buffers */ - for (i = 0; i < MAX_PIPE_BUFFERS; i++) { - struct s2255_pipeinfo *pipe = &dev->pipes[i]; - kfree(pipe->transfer_buffer); - } + /* release transfer buffer */ + kfree(dev->pipe.transfer_buffer); return 0; } @@ -2248,9 +2378,8 @@ static void read_pipe_completion(struct urb *purb) struct s2255_dev *dev; int status; int pipe; - pipe_info = purb->context; - dprintk(100, "read pipe completion %p, status %d\n", purb, + dprintk(100, "%s: urb:%p, status %d\n", __func__, purb, purb->status); if (pipe_info == NULL) { dev_err(&purb->dev->dev, "no context!\n"); @@ -2265,13 +2394,13 @@ static void read_pipe_completion(struct urb *purb) status = purb->status; /* if shutting down, do not resubmit, exit immediately */ if (status == -ESHUTDOWN) { - dprintk(2, "read_pipe_completion: err shutdown\n"); + dprintk(2, "%s: err shutdown\n", __func__); pipe_info->err_count++; return; } if (pipe_info->state == 0) { - dprintk(2, "exiting USB pipe"); + dprintk(2, "%s: exiting USB pipe", __func__); return; } @@ -2279,7 +2408,7 @@ static void read_pipe_completion(struct urb *purb) s2255_read_video_callback(dev, pipe_info); else { pipe_info->err_count++; - dprintk(1, "s2255drv: failed URB %d\n", status); + dprintk(1, "%s: failed URB %d\n", __func__, status); } pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); @@ -2295,7 +2424,7 @@ static void read_pipe_completion(struct urb *purb) dev_err(&dev->udev->dev, "error submitting urb\n"); } } else { - dprintk(2, "read pipe complete state 0\n"); + dprintk(2, "%s :complete state 0\n", __func__); } return; } @@ -2304,35 +2433,28 @@ static int s2255_start_readpipe(struct s2255_dev *dev) { int pipe; int retval; - int i; - struct s2255_pipeinfo *pipe_info = dev->pipes; + struct s2255_pipeinfo *pipe_info = &dev->pipe; pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); - dprintk(2, "start pipe IN %d\n", dev->read_endpoint); - - for (i = 0; i < MAX_PIPE_BUFFERS; i++) { - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) { - dev_err(&dev->udev->dev, - "ReadStream: Unable to alloc URB\n"); - return -ENOMEM; - } - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->cur_transfer_size, - read_pipe_completion, pipe_info); - - dprintk(4, "submitting URB %p\n", pipe_info->stream_urb); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR "s2255: start read pipe failed\n"); - return retval; - } + dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint); + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&dev->udev->dev, + "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR "s2255: start read pipe failed\n"); + return retval; } - return 0; } @@ -2347,10 +2469,7 @@ static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) dprintk(2, "start acquire failed, bad channel %lu\n", chn); return -1; } - chn_rev = G_chnmap[chn]; - dprintk(1, "S2255: start acquire %lu \n", chn); - buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -2366,9 +2485,9 @@ static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) } /* send the start command */ - *(u32 *) buffer = IN_DATA_TOKEN; - *((u32 *) buffer + 1) = (u32) chn_rev; - *((u32 *) buffer + 2) = (u32) CMD_START; + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_START; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (res != 0) dev_err(&dev->udev->dev, "CMD_START error\n"); @@ -2383,65 +2502,41 @@ static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) 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]; - buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); return -ENOMEM; } - /* send the stop command */ - dprintk(4, "stop acquire %lu\n", chn); - *(u32 *) buffer = IN_DATA_TOKEN; - *((u32 *) buffer + 1) = (u32) chn_rev; - *((u32 *) buffer + 2) = CMD_STOP; + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_STOP; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (res != 0) dev_err(&dev->udev->dev, "CMD_STOP error\n"); - - dprintk(4, "stop acquire: releasing states \n"); - kfree(buffer); dev->b_acquire[chn] = 0; - + dprintk(4, "%s: chn %lu, res %d\n", __func__, chn, res); return res; } static void s2255_stop_readpipe(struct s2255_dev *dev) { - int j; - - if (dev == NULL) { - s2255_dev_err(&dev->udev->dev, "invalid device\n"); - return; - } - dprintk(4, "stop read pipe\n"); - for (j = 0; j < MAX_PIPE_BUFFERS; j++) { - struct s2255_pipeinfo *pipe_info = &dev->pipes[j]; - if (pipe_info) { - if (pipe_info->state == 0) - continue; - pipe_info->state = 0; - } - } + struct s2255_pipeinfo *pipe = &dev->pipe; - for (j = 0; j < MAX_PIPE_BUFFERS; j++) { - struct s2255_pipeinfo *pipe_info = &dev->pipes[j]; - if (pipe_info->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe_info->stream_urb); - usb_free_urb(pipe_info->stream_urb); - pipe_info->stream_urb = NULL; - } + pipe->state = 0; + if (pipe->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe->stream_urb); + usb_free_urb(pipe->stream_urb); + pipe->stream_urb = NULL; } - dprintk(2, "s2255 stop read pipe: %d\n", j); + dprintk(4, "%s", __func__); return; } @@ -2473,32 +2568,28 @@ static int s2255_probe(struct usb_interface *interface, int retval = -ENOMEM; __le32 *pdata; int fw_size; - - dprintk(2, "s2255: probe\n"); - + dprintk(2, "%s\n", __func__); /* allocate memory for our device state and initialize it to zero */ dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); if (dev == NULL) { s2255_dev_err(&interface->dev, "out of memory\n"); - goto error; + return -ENOMEM; } - + atomic_set(&dev->channels, 0); + dev->pid = id->idProduct; dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) - goto error; - + goto errorFWDATA1; mutex_init(&dev->lock); mutex_init(&dev->open_lock); - /* grab usb_device and save it */ dev->udev = usb_get_dev(interface_to_usbdev(interface)); if (dev->udev == NULL) { dev_err(&interface->dev, "null usb device\n"); retval = -ENODEV; - goto error; + goto errorUDEV; } - kref_init(&dev->kref); - dprintk(1, "dev: %p, kref: %p udev %p interface %p\n", dev, &dev->kref, + dprintk(1, "dev: %p, udev %p interface %p\n", dev, dev->udev, interface); dev->interface = interface; /* set up the endpoint information */ @@ -2514,39 +2605,33 @@ static int s2255_probe(struct usb_interface *interface, if (!dev->read_endpoint) { dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); - goto error; + goto errorEP; } - - /* set intfdata */ - usb_set_intfdata(interface, dev); - - dprintk(100, "after intfdata %p\n", dev); - init_timer(&dev->timer); dev->timer.function = s2255_timer; dev->timer.data = (unsigned long)dev->fw_data; - init_waitqueue_head(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) { init_waitqueue_head(&dev->wait_setmode[i]); - + init_waitqueue_head(&dev->wait_vidstatus[i]); + } dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->fw_data->fw_urb) { dev_err(&interface->dev, "out of memory!\n"); - goto error; + goto errorFWURB; } + dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); if (!dev->fw_data->pfw_data) { dev_err(&interface->dev, "out of memory!\n"); - goto error; + goto errorFWDATA2; } /* load the first chunk */ if (request_firmware(&dev->fw_data->fw, FIRMWARE_FILE_NAME, &dev->udev->dev)) { printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); - goto error; + goto errorREQFW; } /* check the firmware is valid */ fw_size = dev->fw_data->fw->size; @@ -2555,59 +2640,80 @@ static int s2255_probe(struct usb_interface *interface, if (*pdata != S2255_FW_MARKER) { printk(KERN_INFO "Firmware invalid.\n"); retval = -ENODEV; - goto error; + goto errorFWMARKER; } else { /* make sure firmware is the latest */ __le32 *pRel; pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + dev->dsp_fw_ver = *pRel; + if (*pRel < S2255_CUR_DSP_FWVER) + 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); } - /* loads v4l specific */ - s2255_probe_v4l(dev); usb_reset_device(dev->udev); /* load 2255 board specific */ retval = s2255_board_init(dev); if (retval) - goto error; - - dprintk(4, "before probe done %p\n", dev); + goto errorBOARDINIT; spin_lock_init(&dev->slock); - s2255_fwload_start(dev, 0); + /* loads v4l specific */ + retval = s2255_probe_v4l(dev); + if (retval) + goto errorBOARDINIT; dev_info(&interface->dev, "Sensoray 2255 detected\n"); return 0; -error: +errorBOARDINIT: + s2255_board_shutdown(dev); +errorFWMARKER: + release_firmware(dev->fw_data->fw); +errorREQFW: + kfree(dev->fw_data->pfw_data); +errorFWDATA2: + usb_free_urb(dev->fw_data->fw_urb); +errorFWURB: + del_timer(&dev->timer); +errorEP: + usb_put_dev(dev->udev); +errorUDEV: + kfree(dev->fw_data); + mutex_destroy(&dev->open_lock); + mutex_destroy(&dev->lock); +errorFWDATA1: + kfree(dev); + printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval); return retval; } /* disconnect routine. when board is removed physically or with rmmod */ static void s2255_disconnect(struct usb_interface *interface) { - struct s2255_dev *dev = NULL; + struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); int i; - dprintk(1, "s2255: disconnect interface %p\n", interface); - dev = usb_get_intfdata(interface); - - /* - * wake up any of the timers to allow open_lock to be - * acquired sooner - */ + int channels = atomic_read(&dev->channels); + v4l2_device_unregister(&dev->v4l2_dev); + /*see comments in the uvc_driver.c usb disconnect function */ + atomic_inc(&dev->channels); + /* unregister each video device. */ + for (i = 0; i < channels; i++) { + if (video_is_registered(&dev->vdev[i])) + video_unregister_device(&dev->vdev[i]); + } + /* 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]); } - - mutex_lock(&dev->open_lock); - usb_set_intfdata(interface, NULL); - mutex_unlock(&dev->open_lock); - - if (dev) { - kref_put(&dev->kref, s2255_destroy); - dprintk(1, "s2255drv: disconnect\n"); - dev_info(&interface->dev, "s2255usb now disconnected\n"); - } + if (atomic_dec_and_test(&dev->channels)) + s2255_destroy(dev); + dev_info(&interface->dev, "%s\n", __func__); } static struct usb_driver s2255_driver = { @@ -2620,15 +2726,12 @@ static struct usb_driver s2255_driver = { static int __init usb_s2255_init(void) { int result; - /* register this driver with the USB subsystem */ result = usb_register(&s2255_driver); - if (result) pr_err(KBUILD_MODNAME - ": usb_register failed. Error number %d\n", result); - - dprintk(2, "s2255_init: done\n"); + ": usb_register failed. Error number %d\n", result); + dprintk(2, "%s\n", __func__); return result; } diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index c0a7f8a369f..53b6fcde380 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -74,6 +74,7 @@ struct saa711x_state { int contrast; int hue; int sat; + int chroma_agc; int width; int height; u32 ident; @@ -592,7 +593,7 @@ static const unsigned char saa7115_init_misc[] = { R_5D_DID, 0xbd, R_5E_SDID, 0x35, - R_02_INPUT_CNTL_1, 0x84, /* input tuner -> input 4, amplifier active */ + R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, @@ -743,6 +744,7 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct saa711x_state *state = to_state(sd); + u8 val; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -784,7 +786,21 @@ static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) 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); + 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; } @@ -809,6 +825,12 @@ static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_HUE: ctrl->value = state->hue; 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; + break; default: return -EINVAL; } @@ -1069,7 +1091,7 @@ static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_forma saa7115_cfg_vbi_off); } -static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -1078,11 +1100,8 @@ static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ 0, 0, 0, 0 }; - struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced; int i; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) @@ -1098,16 +1117,27 @@ static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return saa711x_g_sliced_fmt(sd, &fmt->fmt.sliced); +} + +static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + saa711x_set_lcr(sd, NULL); + return 0; +} + +static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + saa711x_set_lcr(sd, fmt); + return 0; +} + static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa711x_set_lcr(sd, &fmt->fmt.sliced); - return 0; - } - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - saa711x_set_lcr(sd, NULL); - return 0; - } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1209,6 +1239,10 @@ static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) 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; } @@ -1524,18 +1558,25 @@ static const struct v4l2_subdev_video_ops saa711x_video_ops = { .s_crystal_freq = saa711x_s_crystal_freq, .g_fmt = saa711x_g_fmt, .s_fmt = saa711x_s_fmt, - .g_vbi_data = saa711x_g_vbi_data, - .decode_vbi_line = saa711x_decode_vbi_line, .s_stream = saa711x_s_stream, .querystd = saa711x_querystd, .g_input_status = saa711x_g_input_status, }; +static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .g_sliced_fmt = saa711x_g_sliced_fmt, + .s_sliced_fmt = saa711x_s_sliced_fmt, + .s_raw_fmt = saa711x_s_raw_fmt, +}; + static const struct v4l2_subdev_ops saa711x_ops = { .core = &saa711x_core_ops, .tuner = &saa711x_tuner_ops, .audio = &saa711x_audio_ops, .video = &saa711x_video_ops, + .vbi = &saa711x_vbi_ops, }; /* ----------------------------------------------------------------------- */ @@ -1593,6 +1634,7 @@ static int saa711x_probe(struct i2c_client *client, state->contrast = 64; state->hue = 0; state->sat = 64; + state->chroma_agc = 1; switch (chip_id) { case '1': state->ident = V4L2_IDENT_SAA7111; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 250ef84cf5c..87986ad62f8 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -625,29 +625,33 @@ static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) return saa7127_set_video_enable(sd, enable); } -static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) { struct saa7127_state *state = to_state(sd); - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - - memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); + memset(fmt, 0, sizeof(*fmt)); if (state->vps_enable) - fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; + fmt->service_lines[0][16] = V4L2_SLICED_VPS; if (state->wss_enable) - fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; if (state->cc_enable) { - fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; + fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; } - fmt->fmt.sliced.service_set = + fmt->service_set = (state->vps_enable ? V4L2_SLICED_VPS : 0) | (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); return 0; } +static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return saa7127_g_sliced_fmt(sd, &fmt->fmt.sliced); +} + static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { switch (data->id) { @@ -727,16 +731,21 @@ static const struct v4l2_subdev_core_ops saa7127_core_ops = { }; static const struct v4l2_subdev_video_ops saa7127_video_ops = { - .s_vbi_data = saa7127_s_vbi_data, .g_fmt = saa7127_g_fmt, .s_std_output = saa7127_s_std_output, .s_routing = saa7127_s_routing, .s_stream = saa7127_s_stream, }; +static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_sliced_fmt = saa7127_g_sliced_fmt, +}; + static const struct v4l2_subdev_ops saa7127_ops = { .core = &saa7127_core_ops, .video = &saa7127_video_ops, + .vbi = &saa7127_vbi_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index d48c450ed77..d3bd82ad010 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -1011,8 +1011,6 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) unsigned int idx; int err, addr; - if (snd_BUG_ON(!chip)) - return -EINVAL; strcpy(card->mixername, "SAA7134 Mixer"); for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 297833fb3b4..72700d4e394 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5355,6 +5355,79 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, }, }, + [SAA7134_BOARD_HAWELL_HW_404M7] = { + /* Hawell HW-404M7 & Hawell HW-808M7 */ + /* Bogoslovskiy Viktor <bogovic@bk.ru> */ + .name = "Hawell HW-404M7", + .audio_clock = 0x00200000, + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x389c00, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x01fc00, + } }, + }, + [SAA7134_BOARD_BEHOLD_H7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV H7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_BEHOLD_A7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV A7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, }; @@ -6549,6 +6622,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SAA7134_BOARD_UNKNOWN, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7190, + .driver_data = SAA7134_BOARD_BEHOLD_H7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7090, + .driver_data = SAA7134_BOARD_BEHOLD_A7, },{ /* --- end of list --- */ } @@ -6602,6 +6687,8 @@ static int saa7134_xc5000_callback(struct saa7134_dev *dev, { switch (dev->board) { case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: if (command == XC5000_TUNER_RESET) { /* Down and UP pheripherial RESET pin for reset all chips */ saa_writeb(SAA7134_SPECIAL_MODE, 0x00); @@ -6973,6 +7060,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: @@ -7215,6 +7304,11 @@ int saa7134_board_init2(struct saa7134_dev *dev) printk(KERN_INFO "%s: P7131 analog only, using " "entry of %s\n", dev->name, saa7134_boards[dev->board].name); + + /* IR init has already happened for other cards, so + * we have to catch up. */ + dev->has_remote = SAA7134_REMOTE_GPIO; + saa7134_input_init1(dev); } break; case SAA7134_BOARD_HAUPPAUGE_HVR1150: @@ -7344,6 +7438,23 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; } + case SAA7134_BOARD_BEHOLD_H6: + { + u8 data[] = { 0x09, 0x9f, 0x86, 0x11}; + struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = data, + .len = sizeof(data)}; + + /* The tuner TUNER_PHILIPS_FMD1216MEX_MK3 after hardware */ + /* start has disabled IF and enabled DVB-T. When saa7134 */ + /* scan I2C devices it not detect IF tda9887 and can`t */ + /* watch TV without software reboot. For solve this problem */ + /* switch the tuner to analog TV mode manually. */ + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + printk(KERN_WARNING + "%s: Unable to enable IF of the tuner.\n", + dev->name); + break; + } } /* switch() */ /* initialize tuner */ diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index a7ad7810fdd..90f23188129 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -471,7 +471,7 @@ static char *irqbits[] = { "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3", "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC", "TRIG_ERR", "CONF_ERR", "LOAD_ERR", - "GPIO16?", "GPIO18", "GPIO22", "GPIO23" + "GPIO16", "GPIO18", "GPIO22", "GPIO23" }; #define IRQBITS ARRAY_SIZE(irqbits) @@ -601,12 +601,14 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) /* disable gpio16 IRQ */ printk(KERN_WARNING "%s/irq: looping -- " "clearing GPIO16 enable bit\n",dev->name); - saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N); } else if (report & SAA7134_IRQ_REPORT_GPIO18) { /* disable gpio18 IRQs */ printk(KERN_WARNING "%s/irq: looping -- " "clearing GPIO18 enable bit\n",dev->name); - saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N); } else { /* disable all irqs */ printk(KERN_WARNING "%s/irq: looping -- " @@ -698,11 +700,13 @@ static int saa7134_hw_enable2(struct saa7134_dev *dev) if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) { if (dev->remote->mask_keydown & 0x10000) - irq2_mask |= SAA7134_IRQ2_INTE_GPIO16; - else if (dev->remote->mask_keydown & 0x40000) - irq2_mask |= SAA7134_IRQ2_INTE_GPIO18; - else if (dev->remote->mask_keyup & 0x40000) - irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A; + irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N; + else { /* Allow enabling both IRQ edge triggers */ + if (dev->remote->mask_keydown & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P; + if (dev->remote->mask_keyup & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N; + } } if (dev->has_remote == SAA7134_REMOTE_I2C) { @@ -1227,7 +1231,7 @@ static int saa7134_resume(struct pci_dev *pci_dev) if (card_has_mpeg(dev)) saa7134_ts_init_hw(dev); if (dev->remote) - saa7134_ir_start(dev, dev->remote); + saa7134_ir_start(dev); saa7134_hw_enable1(dev); msleep(100); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 4ab4a987c9b..31e82be1b7e 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1532,6 +1532,15 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, &behold_x7_tunerconfig); } break; + case SAA7134_BOARD_BEHOLD_H7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: /* Zarlink ZL10313 */ diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 58a0cdc8414..e5565e2fd42 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -28,6 +28,8 @@ #include "saa7134-reg.h" #include "saa7134.h" +#define MODULE_NAME "saa7134" + static unsigned int disable_ir; module_param(disable_ir, int, 0444); MODULE_PARM_DESC(disable_ir,"disable infrared remote support"); @@ -66,6 +68,7 @@ MODULE_PARM_DESC(disable_other_ir, "disable full codes of " /* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */ static int saa7134_rc5_irq(struct saa7134_dev *dev); static int saa7134_nec_irq(struct saa7134_dev *dev); +static int saa7134_raw_decode_irq(struct saa7134_dev *dev); static void nec_task(unsigned long data); static void saa7134_nec_timer(unsigned long data); @@ -397,14 +400,23 @@ static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) void saa7134_input_irq(struct saa7134_dev *dev) { - struct card_ir *ir = dev->remote; + struct card_ir *ir; + + if (!dev || !dev->remote) + return; + + ir = dev->remote; + if (!ir->running) + return; if (ir->nec_gpio) { saa7134_nec_irq(dev); - } else if (!ir->polling && !ir->rc5_gpio) { + } else if (!ir->polling && !ir->rc5_gpio && !ir->raw_decode) { build_key(dev); } else if (ir->rc5_gpio) { saa7134_rc5_irq(dev); + } else if (ir->raw_decode) { + saa7134_raw_decode_irq(dev); } } @@ -417,8 +429,32 @@ static void saa7134_input_timer(unsigned long data) mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } -void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) +void ir_raw_decode_timer_end(unsigned long data) +{ + struct saa7134_dev *dev = (struct saa7134_dev *)data; + struct card_ir *ir = dev->remote; + + ir_raw_event_handle(dev->remote->dev); + + ir->active = 0; +} + +static int __saa7134_ir_start(void *priv) { + struct saa7134_dev *dev = priv; + struct card_ir *ir; + + if (!dev) + return -EINVAL; + + ir = dev->remote; + if (!ir) + return -EINVAL; + + if (ir->running) + return 0; + + ir->running = 1; if (ir->polling) { setup_timer(&ir->timer, saa7134_input_timer, (unsigned long)dev); @@ -441,26 +477,125 @@ void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) setup_timer(&ir->timer_keyup, saa7134_nec_timer, (unsigned long)dev); tasklet_init(&ir->tlet, nec_task, (unsigned long)dev); + } else if (ir->raw_decode) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = ir_raw_decode_timer_end; + ir->timer_end.data = (unsigned long)dev; + ir->active = 0; } + + return 0; } -void saa7134_ir_stop(struct saa7134_dev *dev) +static void __saa7134_ir_stop(void *priv) { + struct saa7134_dev *dev = priv; + struct card_ir *ir; + + if (!dev) + return; + + ir = dev->remote; + if (!ir) + return; + + if (!ir->running) + return; if (dev->remote->polling) del_timer_sync(&dev->remote->timer); + else if (ir->rc5_gpio) + del_timer_sync(&ir->timer_end); + else if (ir->nec_gpio) + tasklet_kill(&ir->tlet); + else if (ir->raw_decode) { + del_timer_sync(&ir->timer_end); + ir->active = 0; + } + + ir->running = 0; + + return; +} + +int saa7134_ir_start(struct saa7134_dev *dev) +{ + if (dev->remote->users) + return __saa7134_ir_start(dev); + + return 0; +} + +void saa7134_ir_stop(struct saa7134_dev *dev) +{ + if (dev->remote->users) + __saa7134_ir_stop(dev); +} + +static int saa7134_ir_open(void *priv) +{ + struct saa7134_dev *dev = priv; + + dev->remote->users++; + return __saa7134_ir_start(dev); +} + +static void saa7134_ir_close(void *priv) +{ + struct saa7134_dev *dev = priv; + + dev->remote->users--; + if (!dev->remote->users) + __saa7134_ir_stop(dev); +} + + +int saa7134_ir_change_protocol(void *priv, u64 ir_type) +{ + struct saa7134_dev *dev = priv; + struct card_ir *ir = dev->remote; + u32 nec_gpio, rc5_gpio; + + if (ir_type == IR_TYPE_RC5) { + dprintk("Changing protocol to RC5\n"); + nec_gpio = 0; + rc5_gpio = 1; + } else if (ir_type == IR_TYPE_NEC) { + dprintk("Changing protocol to NEC\n"); + nec_gpio = 1; + rc5_gpio = 0; + } else { + dprintk("IR protocol type %ud is not supported\n", + (unsigned)ir_type); + return -EINVAL; + } + + if (ir->running) { + saa7134_ir_stop(dev); + ir->nec_gpio = nec_gpio; + ir->rc5_gpio = rc5_gpio; + saa7134_ir_start(dev); + } else { + ir->nec_gpio = nec_gpio; + ir->rc5_gpio = rc5_gpio; + } + + return 0; } int saa7134_input_init1(struct saa7134_dev *dev) { struct card_ir *ir; struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; u32 mask_keycode = 0; u32 mask_keydown = 0; u32 mask_keyup = 0; int polling = 0; int rc5_gpio = 0; int nec_gpio = 0; + int raw_decode = 0; + int allow_protocol_change = 0; u64 ir_type = IR_TYPE_OTHER; int err; @@ -476,27 +611,27 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYTVPLATINUM_FM: case SAA7134_BOARD_FLYTVPLATINUM_MINI2: case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: - ir_codes = &ir_codes_flyvideo_table; + ir_codes = RC_MAP_FLYVIDEO; mask_keycode = 0xEC00000; mask_keydown = 0x0040000; break; case SAA7134_BOARD_CINERGY400: case SAA7134_BOARD_CINERGY600: case SAA7134_BOARD_CINERGY600_MK3: - ir_codes = &ir_codes_cinergy_table; + ir_codes = RC_MAP_CINERGY; mask_keycode = 0x00003f; mask_keyup = 0x040000; break; case SAA7134_BOARD_ECS_TVP3XP: case SAA7134_BOARD_ECS_TVP3XP_4CB5: - ir_codes = &ir_codes_eztv_table; + ir_codes = RC_MAP_EZTV; mask_keycode = 0x00017c; mask_keyup = 0x000002; polling = 50; // ms break; case SAA7134_BOARD_KWORLD_XPERT: case SAA7134_BOARD_AVACSSMARTTV: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; mask_keycode = 0x00001F; mask_keyup = 0x000020; polling = 50; // ms @@ -513,7 +648,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_GO_007_FM: case SAA7134_BOARD_AVERMEDIA_M102: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: - ir_codes = &ir_codes_avermedia_table; + ir_codes = RC_MAP_AVERMEDIA; mask_keycode = 0x0007C8; mask_keydown = 0x000010; polling = 50; // ms @@ -522,14 +657,15 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); break; case SAA7134_BOARD_AVERMEDIA_M135A: - ir_codes = &ir_codes_avermedia_m135a_table; - mask_keydown = 0x0040000; - mask_keycode = 0x00013f; - nec_gpio = 1; + ir_codes = RC_MAP_AVERMEDIA_M135A_RM_JX; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = 1; break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: - ir_codes = &ir_codes_avermedia_table; + ir_codes = RC_MAP_AVERMEDIA; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; // ms @@ -538,7 +674,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; case SAA7134_BOARD_AVERMEDIA_A16D: - ir_codes = &ir_codes_avermedia_a16d_table; + ir_codes = RC_MAP_AVERMEDIA_A16D; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; /* ms */ @@ -547,14 +683,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; case SAA7134_BOARD_KWORLD_TERMINATOR: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; mask_keycode = 0x00001f; mask_keyup = 0x000060; polling = 50; // ms break; case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: - ir_codes = &ir_codes_manli_table; + ir_codes = RC_MAP_MANLI; mask_keycode = 0x001f00; mask_keyup = 0x004000; polling = 50; /* ms */ @@ -574,25 +710,25 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_BEHOLD_507RDS_MK3: case SAA7134_BOARD_BEHOLD_507RDS_MK5: - ir_codes = &ir_codes_manli_table; + ir_codes = RC_MAP_MANLI; mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; /* ms */ break; case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: - ir_codes = &ir_codes_behold_columbus_table; + ir_codes = RC_MAP_BEHOLD_COLUMBUS; mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; // ms break; case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: - ir_codes = &ir_codes_pctv_sedna_table; + ir_codes = RC_MAP_PCTV_SEDNA; mask_keycode = 0x001f00; mask_keyup = 0x004000; polling = 50; // ms break; case SAA7134_BOARD_GOTVIEW_7135: - ir_codes = &ir_codes_gotview7135_table; + ir_codes = RC_MAP_GOTVIEW7135; mask_keycode = 0x0003CC; mask_keydown = 0x000010; polling = 5; /* ms */ @@ -601,80 +737,80 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_TV_PVR: case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: - ir_codes = &ir_codes_videomate_tv_pvr_table; + ir_codes = RC_MAP_VIDEOMATE_TV_PVR; mask_keycode = 0x00003F; mask_keyup = 0x400000; polling = 50; // ms break; case SAA7134_BOARD_PROTEUS_2309: - ir_codes = &ir_codes_proteus_2309_table; + ir_codes = RC_MAP_PROTEUS_2309; mask_keycode = 0x00007F; mask_keyup = 0x000080; polling = 50; // ms break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: - ir_codes = &ir_codes_videomate_tv_pvr_table; + ir_codes = RC_MAP_VIDEOMATE_TV_PVR; mask_keycode = 0x003F00; mask_keyup = 0x040000; break; case SAA7134_BOARD_FLYDVBS_LR300: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_FLYDVBTDUO: - ir_codes = &ir_codes_flydvb_table; + ir_codes = RC_MAP_FLYDVB; mask_keycode = 0x0001F00; mask_keydown = 0x0040000; break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: - ir_codes = &ir_codes_asus_pc39_table; + ir_codes = RC_MAP_ASUS_PC39; mask_keydown = 0x0040000; rc5_gpio = 1; break; case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: - ir_codes = &ir_codes_encore_enltv_table; + ir_codes = RC_MAP_ENCORE_ENLTV; mask_keycode = 0x00007f; mask_keyup = 0x040000; polling = 50; // ms break; case SAA7134_BOARD_ENCORE_ENLTV_FM53: - ir_codes = &ir_codes_encore_enltv_fm53_table; + ir_codes = RC_MAP_ENCORE_ENLTV_FM53; mask_keydown = 0x0040000; mask_keycode = 0x00007f; nec_gpio = 1; break; case SAA7134_BOARD_10MOONSTVMASTER3: - ir_codes = &ir_codes_encore_enltv_table; + ir_codes = RC_MAP_ENCORE_ENLTV; mask_keycode = 0x5f80000; mask_keyup = 0x8000000; polling = 50; //ms break; case SAA7134_BOARD_GENIUS_TVGO_A11MCE: - ir_codes = &ir_codes_genius_tvgo_a11mce_table; + ir_codes = RC_MAP_GENIUS_TVGO_A11MCE; mask_keycode = 0xff; mask_keydown = 0xf00000; polling = 50; /* ms */ break; case SAA7134_BOARD_REAL_ANGEL_220: - ir_codes = &ir_codes_real_audio_220_32_keys_table; + ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS; mask_keycode = 0x3f00; mask_keyup = 0x4000; polling = 50; /* ms */ break; case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: - ir_codes = &ir_codes_kworld_plus_tv_analog_table; + ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG; mask_keycode = 0x7f; polling = 40; /* ms */ break; case SAA7134_BOARD_VIDEOMATE_S350: - ir_codes = &ir_codes_videomate_s350_table; + ir_codes = RC_MAP_VIDEOMATE_S350; mask_keycode = 0x003f00; mask_keydown = 0x040000; break; case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; mask_keycode = 0x5f00; mask_keyup = 0x020000; polling = 50; /* ms */ @@ -695,6 +831,9 @@ int saa7134_input_init1(struct saa7134_dev *dev) } ir->dev = input_dev; + dev->remote = ir; + + ir->running = 0; /* init hardware-specific stuff */ ir->mask_keycode = mask_keycode; @@ -703,6 +842,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir->polling = polling; ir->rc5_gpio = rc5_gpio; ir->nec_gpio = nec_gpio; + ir->raw_decode = raw_decode; /* init input device */ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)", @@ -710,6 +850,19 @@ int saa7134_input_init1(struct saa7134_dev *dev) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + + ir->props.priv = dev; + ir->props.open = saa7134_ir_open; + ir->props.close = saa7134_ir_close; + + if (raw_decode) + ir->props.driver_type = RC_DRIVER_IR_RAW; + + if (!raw_decode && allow_protocol_change) { + ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; + ir->props.change_protocol = saa7134_ir_change_protocol; + } + err = ir_input_init(input_dev, &ir->ir, ir_type); if (err < 0) goto err_out_free; @@ -727,12 +880,9 @@ int saa7134_input_init1(struct saa7134_dev *dev) } input_dev->dev.parent = &dev->pci->dev; - dev->remote = ir; - saa7134_ir_start(dev, ir); - - err = ir_input_register(ir->dev, ir_codes, NULL); + err = ir_input_register(ir->dev, ir_codes, &ir->props, MODULE_NAME); if (err) - goto err_out_stop; + goto err_out_free; /* the remote isn't as bouncy as a keyboard */ ir->dev->rep[REP_DELAY] = repeat_delay; @@ -740,10 +890,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) return 0; - err_out_stop: - saa7134_ir_stop(dev); +err_out_free: dev->remote = NULL; - err_out_free: kfree(ir); return err; } @@ -787,24 +935,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "Pinnacle PCTV"; if (pinnacle_remote == 0) { dev->init_data.get_key = get_key_pinnacle_color; - dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; + dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR; info.addr = 0x47; } else { dev->init_data.get_key = get_key_pinnacle_grey; - dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->init_data.name = "Purple TV"; dev->init_data.get_key = get_key_purpletv; - dev->init_data.ir_codes = &ir_codes_purpletv_table; + dev->init_data.ir_codes = RC_MAP_PURPLETV; info.addr = 0x7a; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: dev->init_data.name = "MSI TV@nywhere Plus"; dev->init_data.get_key = get_key_msi_tvanywhere_plus; - dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; + dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS; info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from @@ -818,7 +966,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_HAUPPAUGE_HVR1110: dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; - dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE_NEW; info.addr = 0x71; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: @@ -834,9 +982,12 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: dev->init_data.name = "BeholdTV"; dev->init_data.get_key = get_key_beholdm6xx; - dev->init_data.ir_codes = &ir_codes_behold_table; + dev->init_data.ir_codes = RC_MAP_BEHOLD; + dev->init_data.type = IR_TYPE_NEC; info.addr = 0x2d; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: @@ -846,7 +997,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVB_TRIO: dev->init_data.name = "FlyDVB Trio"; dev->init_data.get_key = get_key_flydvb_trio; - dev->init_data.ir_codes = &ir_codes_flydvb_table; + dev->init_data.ir_codes = RC_MAP_FLYDVB; info.addr = 0x0b; break; default: @@ -859,6 +1010,33 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) i2c_new_device(&dev->i2c_adap, &info); } +static int saa7134_raw_decode_irq(struct saa7134_dev *dev) +{ + struct card_ir *ir = dev->remote; + unsigned long timeout; + int space; + + /* Generate initial event */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown; + ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE); + + + /* + * Wait 15 ms from the start of the first IR event before processing + * the event. This time is enough for NEC protocol. May need adjustments + * to work with other protocols. + */ + if (!ir->active) { + timeout = jiffies + jiffies_to_msecs(15); + mod_timer(&ir->timer_end, timeout); + ir->active = 1; + } + + return 1; +} + static int saa7134_rc5_irq(struct saa7134_dev *dev) { struct card_ir *ir = dev->remote; @@ -901,7 +1079,6 @@ static int saa7134_rc5_irq(struct saa7134_dev *dev) return 1; } - /* On NEC protocol, One has 2.25 ms, and zero has 1.125 ms The first pulse (start) has 9 + 4.5 ms */ @@ -1011,14 +1188,14 @@ static void nec_task(unsigned long data) /* Keep repeating the last key */ mod_timer(&ir->timer_keyup, jiffies + msecs_to_jiffies(150)); - saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); } static int saa7134_nec_irq(struct saa7134_dev *dev) { struct card_ir *ir = dev->remote; - saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); tasklet_schedule(&ir->tlet); return 1; diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h index cf89d96d729..e7e0af101fa 100644 --- a/drivers/media/video/saa7134/saa7134-reg.h +++ b/drivers/media/video/saa7134/saa7134-reg.h @@ -112,17 +112,17 @@ #define SAA7134_IRQ1_INTE_RA0_0 (1 << 0) #define SAA7134_IRQ2 (0x2c8 >> 2) -#define SAA7134_IRQ2_INTE_GPIO23A (1 << 17) -#define SAA7134_IRQ2_INTE_GPIO23 (1 << 16) -#define SAA7134_IRQ2_INTE_GPIO22A (1 << 15) -#define SAA7134_IRQ2_INTE_GPIO22 (1 << 14) -#define SAA7134_IRQ2_INTE_GPIO18A (1 << 13) -#define SAA7134_IRQ2_INTE_GPIO18 (1 << 12) -#define SAA7134_IRQ2_INTE_GPIO16 (1 << 11) /* not certain */ -#define SAA7134_IRQ2_INTE_SC2 (1 << 10) -#define SAA7134_IRQ2_INTE_SC1 (1 << 9) -#define SAA7134_IRQ2_INTE_SC0 (1 << 8) -#define SAA7134_IRQ2_INTE_DEC5 (1 << 7) +#define SAA7134_IRQ2_INTE_GPIO23_N (1 << 17) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO23_P (1 << 16) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO22_N (1 << 15) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO22_P (1 << 14) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO18_N (1 << 13) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO18_P (1 << 12) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO16_N (1 << 11) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO16_P (1 << 10) /* positive edge */ +#define SAA7134_IRQ2_INTE_SC2 (1 << 9) +#define SAA7134_IRQ2_INTE_SC1 (1 << 8) +#define SAA7134_IRQ2_INTE_SC0 (1 << 7) #define SAA7134_IRQ2_INTE_DEC4 (1 << 6) #define SAA7134_IRQ2_INTE_DEC3 (1 << 5) #define SAA7134_IRQ2_INTE_DEC2 (1 << 4) @@ -135,7 +135,7 @@ #define SAA7134_IRQ_REPORT_GPIO23 (1 << 17) #define SAA7134_IRQ_REPORT_GPIO22 (1 << 16) #define SAA7134_IRQ_REPORT_GPIO18 (1 << 15) -#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) /* not certain */ +#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) #define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13) #define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12) #define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11) diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 31138d3e51b..45f0ac8f3c0 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1180,7 +1180,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str That needs to be fixed somehow, but for now this is good enough. */ if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -1359,7 +1359,7 @@ static int video_open(struct file *file) fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); fh->width = 720; fh->height = 576; - v4l2_prio_open(&dev->prio,&fh->prio); + v4l2_prio_open(&dev->prio, &fh->prio); videobuf_queue_sg_init(&fh->cap, &video_qops, &dev->pci->dev, &dev->slock, @@ -1502,7 +1502,7 @@ static int video_release(struct file *file) saa7134_pgtable_free(dev->pci,&fh->pt_cap); saa7134_pgtable_free(dev->pci,&fh->pt_vbi); - v4l2_prio_close(&dev->prio,&fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); return 0; @@ -1632,8 +1632,15 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, - &f->fmt.pix.height, 32, maxh, 0, 0); + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -1778,7 +1785,7 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i) struct saa7134_dev *dev = fh->dev; int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; @@ -1832,7 +1839,7 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ That needs to be fixed somehow, but for now this is good enough. */ if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } else if (res_locked(dev, RESOURCE_OVERLAY)) { @@ -2016,7 +2023,7 @@ static int saa7134_s_tuner(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int rx, mode, err; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; @@ -2050,7 +2057,7 @@ static int saa7134_s_frequency(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index bf130967ed1..3962534267b 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -20,7 +20,7 @@ */ #include <linux/version.h> -#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,15) +#define SAA7134_VERSION_CODE KERNEL_VERSION(0, 2, 16) #include <linux/pci.h> #include <linux/i2c.h> @@ -300,6 +300,9 @@ struct saa7134_format { #define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 #define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 #define SAA7134_BOARD_BEHOLD_505RDS_MK3 176 +#define SAA7134_BOARD_HAWELL_HW_404M7 177 +#define SAA7134_BOARD_BEHOLD_H7 178 +#define SAA7134_BOARD_BEHOLD_A7 179 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -809,7 +812,7 @@ int saa7134_input_init1(struct saa7134_dev *dev); void saa7134_input_fini(struct saa7134_dev *dev); void saa7134_input_irq(struct saa7134_dev *dev); void saa7134_probe_i2c_ir(struct saa7134_dev *dev); -void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir); +int saa7134_ir_start(struct saa7134_dev *dev); void saa7134_ir_stop(struct saa7134_dev *dev); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 1ad980f8e77..4ac3b482fbb 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -115,9 +115,20 @@ struct sh_mobile_ceu_dev { }; struct sh_mobile_ceu_cam { - struct v4l2_rect ceu_rect; - unsigned int cam_width; - unsigned int cam_height; + /* CEU offsets within scaled by the CEU camera output */ + unsigned int ceu_left; + unsigned int ceu_top; + /* Client output, as seen by the CEU */ + unsigned int width; + unsigned int height; + /* + * User window from S_CROP / G_CROP, produced by client cropping and + * scaling, CEU scaling and CEU cropping, mapped back onto the client + * input window + */ + struct v4l2_rect subrect; + /* Camera cropping rectangle */ + struct v4l2_rect rect; const struct soc_mbus_pixelfmt *extra_fmt; enum v4l2_mbus_pixelcode code; }; @@ -213,8 +224,8 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, *count = 2; if (pcdev->video_limit) { - while (PAGE_ALIGN(*size) * *count > pcdev->video_limit) - (*count)--; + if (PAGE_ALIGN(*size) * *count > pcdev->video_limit) + *count = pcdev->video_limit / PAGE_ALIGN(*size); } dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); @@ -565,38 +576,36 @@ static u16 calc_scale(unsigned int src, unsigned int *dst) } /* rect is guaranteed to not exceed the scaled camera rectangle */ -static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, - unsigned int out_width, - unsigned int out_height) +static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *rect = &cam->ceu_rect; struct sh_mobile_ceu_dev *pcdev = ici->priv; unsigned int height, width, cdwdr_width, in_width, in_height; unsigned int left_offset, top_offset; u32 camor; - dev_dbg(icd->dev.parent, "Crop %ux%u@%u:%u\n", - rect->width, rect->height, rect->left, rect->top); + dev_geo(icd->dev.parent, "Crop %ux%u@%u:%u\n", + icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); - left_offset = rect->left; - top_offset = rect->top; + left_offset = cam->ceu_left; + top_offset = cam->ceu_top; + /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */ if (pcdev->image_mode) { - in_width = rect->width; + in_width = cam->width; if (!pcdev->is_16bit) { in_width *= 2; left_offset *= 2; } - width = out_width; - cdwdr_width = out_width; + width = icd->user_width; + cdwdr_width = icd->user_width; } else { - int bytes_per_line = soc_mbus_bytes_per_line(out_width, + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); unsigned int w_factor; - width = out_width; + width = icd->user_width; switch (icd->current_fmt->host_fmt->packing) { case SOC_MBUS_PACKING_2X8_PADHI: @@ -606,17 +615,17 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, w_factor = 1; } - in_width = rect->width * w_factor; + in_width = cam->width * w_factor; left_offset = left_offset * w_factor; if (bytes_per_line < 0) - cdwdr_width = out_width; + cdwdr_width = icd->user_width; else cdwdr_width = bytes_per_line; } - height = out_height; - in_height = rect->height; + height = icd->user_height; + in_height = cam->height; if (V4L2_FIELD_NONE != pcdev->field) { height /= 2; in_height /= 2; @@ -775,9 +784,10 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, } ceu_write(pcdev, CAIFR, value); - sh_mobile_ceu_set_rect(icd, icd->user_width, icd->user_height); + sh_mobile_ceu_set_rect(icd); mdelay(1); + dev_geo(icd->dev.parent, "CFLCR 0x%x\n", pcdev->cflcr); ceu_write(pcdev, CFLCR, pcdev->cflcr); /* @@ -866,6 +876,8 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) fmt->packing == SOC_MBUS_PACKING_EXTEND16); } +static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); + static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { @@ -894,10 +906,55 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, 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 */ + + /* Cache current client geometry */ + ret = client_g_rect(sd, &rect); + if (ret < 0) + return ret; + + /* First time */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + while ((mf.width > 2560 || mf.height > 1920) && shift < 4) { + /* Try 2560x1920, 1280x960, 640x480, 320x240 */ + mf.width = 2560 >> shift; + mf.height = 1920 >> shift; + ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + if (ret < 0) + return ret; + shift++; + } + + if (shift == 4) { + dev_err(dev, "Failed to configure the client below %ux%x\n", + mf.width, mf.height); + return -EIO; + } + + dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height); + cam = kzalloc(sizeof(*cam), GFP_KERNEL); if (!cam) return -ENOMEM; + /* We are called with current camera crop, initialise subrect with it */ + cam->rect = rect; + cam->subrect = rect; + + cam->width = mf.width; + cam->height = mf.height; + + cam->width = mf.width; + cam->height = mf.height; + icd->host_priv = cam; } else { cam = icd->host_priv; @@ -979,16 +1036,12 @@ static unsigned int scale_down(unsigned int size, unsigned int scale) return (size * 4096 + scale / 2) / scale; } -static unsigned int scale_up(unsigned int size, unsigned int scale) -{ - return (size * scale + 2048) / 4096; -} - static unsigned int calc_generic_scale(unsigned int input, unsigned int output) { return (input * 4096 + output / 2) / output; } +/* Get and store current client crop */ static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) { struct v4l2_crop crop; @@ -1007,25 +1060,51 @@ static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = v4l2_subdev_call(sd, video, cropcap, &cap); - if (ret < 0) - return ret; - - *rect = cap.defrect; + if (!ret) + *rect = cap.defrect; return ret; } +/* Client crop has changed, update our sub-rectangle to remain within the area */ +static void update_subrect(struct sh_mobile_ceu_cam *cam) +{ + struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect; + + if (rect->width < subrect->width) + subrect->width = rect->width; + + if (rect->height < subrect->height) + subrect->height = rect->height; + + if (rect->left > subrect->left) + subrect->left = rect->left; + else if (rect->left + rect->width > + subrect->left + subrect->width) + subrect->left = rect->left + rect->width - + subrect->width; + + if (rect->top > subrect->top) + subrect->top = rect->top; + else if (rect->top + rect->height > + subrect->top + subrect->height) + subrect->top = rect->top + rect->height - + subrect->height; +} + /* * The common for both scaling and cropping iterative approach is: * 1. try if the client can produce exactly what requested by the user * 2. if (1) failed, try to double the client image until we get one big enough * 3. if (2) failed, try to request the maximum image */ -static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, +static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, struct v4l2_crop *cam_crop) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; struct device *dev = sd->v4l2_dev->dev; + struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_cropcap cap; int ret; unsigned int width, height; @@ -1041,13 +1120,14 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, */ if (!memcmp(rect, cam_rect, sizeof(*rect))) { /* Even if camera S_CROP failed, but camera rectangle matches */ - dev_dbg(dev, "Camera S_CROP successful for %ux%u@%u:%u\n", + dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", rect->width, rect->height, rect->left, rect->top); + cam->rect = *cam_rect; return 0; } /* Try to fix cropping, that camera hasn't managed to set */ - dev_geo(dev, "Fix camera S_CROP for %ux%u@%u:%u to %ux%u@%u:%u\n", + dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top, rect->width, rect->height, rect->left, rect->top); @@ -1057,6 +1137,7 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, if (ret < 0) return ret; + /* Put user requested rectangle within sensor bounds */ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, cap.bounds.width); soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, @@ -1069,6 +1150,10 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, width = max(cam_rect->width, 2); height = max(cam_rect->height, 2); + /* + * Loop as long as sensor is not covering the requested rectangle and + * is still within its bounds + */ while (!ret && (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) && (cap.bounds.width > width || cap.bounds.height > height)) { @@ -1086,6 +1171,7 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, * target left, set it to the middle point between the current * left and minimum left. But that would add too much * complexity: we would have to iterate each border separately. + * Instead we just drop to the left and top bounds. */ if (cam_rect->left > rect->left) cam_rect->left = cap.bounds.left; @@ -1103,7 +1189,7 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, v4l2_subdev_call(sd, video, s_crop, cam_crop); ret = client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for %ux%u@%u:%u\n", ret, + dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } @@ -1117,82 +1203,24 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, *cam_rect = cap.bounds; v4l2_subdev_call(sd, video, s_crop, cam_crop); ret = client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for max %ux%u@%u:%u\n", ret, + dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } - return ret; -} - -static int get_camera_scales(struct v4l2_subdev *sd, struct v4l2_rect *rect, - unsigned int *scale_h, unsigned int *scale_v) -{ - struct v4l2_mbus_framefmt mf; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); - if (ret < 0) - return ret; - - *scale_h = calc_generic_scale(rect->width, mf.width); - *scale_v = calc_generic_scale(rect->height, mf.height); - - return 0; -} - -static int get_camera_subwin(struct soc_camera_device *icd, - struct v4l2_rect *cam_subrect, - unsigned int cam_hscale, unsigned int cam_vscale) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *ceu_rect = &cam->ceu_rect; - - if (!ceu_rect->width) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; - struct v4l2_mbus_framefmt mf; - int ret; - /* First time */ - - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); - if (ret < 0) - return ret; - - dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height); - - if (mf.width > 2560) { - ceu_rect->width = 2560; - ceu_rect->left = (mf.width - 2560) / 2; - } else { - ceu_rect->width = mf.width; - ceu_rect->left = 0; - } - - if (mf.height > 1920) { - ceu_rect->height = 1920; - ceu_rect->top = (mf.height - 1920) / 2; - } else { - ceu_rect->height = mf.height; - ceu_rect->top = 0; - } - - dev_geo(dev, "initialised CEU rect %ux%u@%u:%u\n", - ceu_rect->width, ceu_rect->height, - ceu_rect->left, ceu_rect->top); + if (!ret) { + cam->rect = *cam_rect; + update_subrect(cam); } - cam_subrect->width = scale_up(ceu_rect->width, cam_hscale); - cam_subrect->left = scale_up(ceu_rect->left, cam_hscale); - cam_subrect->height = scale_up(ceu_rect->height, cam_vscale); - cam_subrect->top = scale_up(ceu_rect->top, cam_vscale); - - return 0; + return ret; } +/* Iterative s_mbus_fmt, also updates cached client crop on success */ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) { + struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; @@ -1200,6 +1228,15 @@ 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); + if (ret < 0) + return ret; + + dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); + + if ((width == mf->width && height == mf->height) || !ceu_can_scale) + goto update_cache; + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = v4l2_subdev_call(sd, video, cropcap, &cap); @@ -1209,15 +1246,6 @@ static int client_s_fmt(struct soc_camera_device *icd, max_width = min(cap.bounds.width, 2560); max_height = min(cap.bounds.height, 1920); - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); - if (ret < 0) - return ret; - - dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); - - if ((width == mf->width && height == mf->height) || !ceu_can_scale) - return 0; - /* Camera set a format, but geometry is not precise, try to improve */ tmp_w = mf->width; tmp_h = mf->height; @@ -1239,26 +1267,37 @@ static int client_s_fmt(struct soc_camera_device *icd, } } +update_cache: + /* Update cache */ + ret = client_g_rect(sd, &cam->rect); + if (ret < 0) + return ret; + + update_subrect(cam); + return 0; } /** - * @rect - camera cropped rectangle - * @sub_rect - CEU cropped rectangle, mapped back to camera input area - * @ceu_rect - on output calculated CEU crop rectangle + * @width - on output: user width, mapped back to input + * @height - on output: user height, mapped back to input + * @mf - in- / output camera output window */ -static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, - struct v4l2_rect *sub_rect, struct v4l2_rect *ceu_rect, - struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) +static int client_scale(struct soc_camera_device *icd, + struct v4l2_mbus_framefmt *mf, + unsigned int *width, unsigned int *height, + bool ceu_can_scale) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct sh_mobile_ceu_cam *cam = icd->host_priv; struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf_tmp = *mf; unsigned int scale_h, scale_v; int ret; - /* 5. Apply iterative camera S_FMT for camera user window. */ + /* + * 5. Apply iterative camera S_FMT for camera user window (also updates + * client crop cache and the imaginary sub-rectangle). + */ ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale); if (ret < 0) return ret; @@ -1270,60 +1309,22 @@ static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, /* unneeded - it is already in "mf_tmp" */ - /* 7. Calculate new camera scales. */ - ret = get_camera_scales(sd, rect, &scale_h, &scale_v); - if (ret < 0) - return ret; - - dev_geo(dev, "7: camera scales %u:%u\n", scale_h, scale_v); + /* 7. Calculate new client scales. */ + scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width); + scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height); - cam->cam_width = mf_tmp.width; - cam->cam_height = mf_tmp.height; mf->width = mf_tmp.width; mf->height = mf_tmp.height; mf->colorspace = mf_tmp.colorspace; /* * 8. Calculate new CEU crop - apply camera scales to previously - * calculated "effective" crop. + * updated "effective" crop. */ - ceu_rect->left = scale_down(sub_rect->left, scale_h); - ceu_rect->width = scale_down(sub_rect->width, scale_h); - ceu_rect->top = scale_down(sub_rect->top, scale_v); - ceu_rect->height = scale_down(sub_rect->height, scale_v); + *width = scale_down(cam->subrect.width, scale_h); + *height = scale_down(cam->subrect.height, scale_v); - dev_geo(dev, "8: new CEU rect %ux%u@%u:%u\n", - ceu_rect->width, ceu_rect->height, - ceu_rect->left, ceu_rect->top); - - return 0; -} - -/* Get combined scales */ -static int get_scales(struct soc_camera_device *icd, - unsigned int *scale_h, unsigned int *scale_v) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_crop cam_crop; - unsigned int width_in, height_in; - int ret; - - cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = client_g_rect(sd, &cam_crop.c); - if (ret < 0) - return ret; - - ret = get_camera_scales(sd, &cam_crop.c, scale_h, scale_v); - if (ret < 0) - return ret; - - width_in = scale_up(cam->ceu_rect.width, *scale_h); - height_in = scale_up(cam->ceu_rect.height, *scale_v); - - *scale_h = calc_generic_scale(width_in, icd->user_width); - *scale_v = calc_generic_scale(height_in, icd->user_height); + dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); return 0; } @@ -1342,115 +1343,165 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, struct sh_mobile_ceu_dev *pcdev = ici->priv; struct v4l2_crop cam_crop; struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_crop.c, *ceu_rect = &cam->ceu_rect; + struct v4l2_rect *cam_rect = &cam_crop.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf; - unsigned int scale_comb_h, scale_comb_v, scale_ceu_h, scale_ceu_v, - out_width, out_height; + unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, + out_width, out_height, scale_h, scale_v; + int interm_width, interm_height; u32 capsr, cflcr; int ret; - /* 1. Calculate current combined scales. */ - ret = get_scales(icd, &scale_comb_h, &scale_comb_v); - if (ret < 0) - return ret; + dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, + rect->left, rect->top); - dev_geo(dev, "1: combined scales %u:%u\n", scale_comb_h, scale_comb_v); + /* During camera cropping its output window can change too, stop CEU */ + capsr = capture_save_reset(pcdev); + dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); - /* 2. Apply iterative camera S_CROP for new input window. */ - ret = client_s_crop(sd, a, &cam_crop); + /* 1. - 2. Apply iterative camera S_CROP for new input window. */ + ret = client_s_crop(icd, a, &cam_crop); if (ret < 0) return ret; - dev_geo(dev, "2: camera cropped to %ux%u@%u:%u\n", + dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n", cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); /* On success cam_crop contains current camera crop */ - /* - * 3. If old combined scales applied to new crop produce an impossible - * user window, adjust scales to produce nearest possible window. - */ - out_width = scale_down(rect->width, scale_comb_h); - out_height = scale_down(rect->height, scale_comb_v); - - if (out_width > 2560) - out_width = 2560; - else if (out_width < 2) - out_width = 2; - - if (out_height > 1920) - out_height = 1920; - else if (out_height < 4) - out_height = 4; - - dev_geo(dev, "3: Adjusted output %ux%u\n", out_width, out_height); - - /* 4. Use G_CROP to retrieve actual input window: already in cam_crop */ - - /* - * 5. Using actual input window and calculated combined scales calculate - * camera target output window. - */ - mf.width = scale_down(cam_rect->width, scale_comb_h); - mf.height = scale_down(cam_rect->height, scale_comb_v); - - dev_geo(dev, "5: camera target %ux%u\n", mf.width, mf.height); - - /* 6. - 9. */ - mf.code = cam->code; - mf.field = pcdev->field; - - capsr = capture_save_reset(pcdev); - dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); + /* 3. Retrieve camera output window */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; - /* Make relative to camera rectangle */ - rect->left -= cam_rect->left; - rect->top -= cam_rect->top; + if (mf.width > 2560 || mf.height > 1920) + return -EINVAL; - ret = client_scale(icd, cam_rect, rect, ceu_rect, &mf, - pcdev->image_mode && - V4L2_FIELD_NONE == pcdev->field); + /* Cache camera output window */ + cam->width = mf.width; + cam->height = mf.height; - dev_geo(dev, "6-9: %d\n", ret); + /* 4. Calculate camera scales */ + scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); + scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); - /* 10. Use CEU cropping to crop to the new window. */ - sh_mobile_ceu_set_rect(icd, out_width, out_height); + /* Calculate intermediate window */ + interm_width = scale_down(rect->width, scale_cam_h); + interm_height = scale_down(rect->height, scale_cam_v); - dev_geo(dev, "10: CEU cropped to %ux%u@%u:%u\n", - ceu_rect->width, ceu_rect->height, - ceu_rect->left, ceu_rect->top); + if (pcdev->image_mode) { + out_width = min(interm_width, icd->user_width); + out_height = min(interm_height, icd->user_height); + } else { + out_width = interm_width; + out_height = interm_height; + } /* - * 11. Calculate CEU scales from camera scales from results of (10) and - * user window from (3) + * 5. Calculate CEU scales from camera scales from results of (5) and + * the user window */ - scale_ceu_h = calc_scale(ceu_rect->width, &out_width); - scale_ceu_v = calc_scale(ceu_rect->height, &out_height); + scale_ceu_h = calc_scale(interm_width, &out_width); + scale_ceu_v = calc_scale(interm_height, &out_height); - dev_geo(dev, "11: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); + /* Calculate camera scales */ + scale_h = calc_generic_scale(cam_rect->width, out_width); + scale_v = calc_generic_scale(cam_rect->height, out_height); - /* 12. Apply CEU scales. */ + dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); + + /* Apply CEU scales. */ cflcr = scale_ceu_h | (scale_ceu_v << 16); if (cflcr != pcdev->cflcr) { pcdev->cflcr = cflcr; ceu_write(pcdev, CFLCR, cflcr); } + icd->user_width = out_width; + icd->user_height = out_height; + cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_h) & ~1; + cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_v) & ~1; + + /* 6. Use CEU cropping to crop to the new window. */ + sh_mobile_ceu_set_rect(icd); + + cam->subrect = *rect; + + dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n", + icd->user_width, icd->user_height, + cam->ceu_left, cam->ceu_top); + /* Restore capture */ if (pcdev->active) capsr |= 1; capture_restore(pcdev, capsr); - icd->user_width = out_width; - icd->user_height = out_height; - /* Even if only camera cropping succeeded */ return ret; } +static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, + struct v4l2_crop *a) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->c = cam->subrect; + + return 0; +} + +/* + * Calculate real client output window by applying new scales to the current + * client crop. New scales are calculated from the requested output format and + * CEU crop, mapped backed onto the client input (subrect). + */ +static void calculate_client_output(struct soc_camera_device *icd, + struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct device *dev = icd->dev.parent; + struct v4l2_rect *cam_subrect = &cam->subrect; + unsigned int scale_v, scale_h; + + if (cam_subrect->width == cam->rect.width && + cam_subrect->height == cam->rect.height) { + /* No sub-cropping */ + mf->width = pix->width; + mf->height = pix->height; + return; + } + + /* 1.-2. Current camera scales and subwin - cached. */ + + dev_geo(dev, "2: subwin %ux%u@%u:%u\n", + cam_subrect->width, cam_subrect->height, + cam_subrect->left, cam_subrect->top); + + /* + * 3. Calculate new combined scales from input sub-window to requested + * user window. + */ + + /* + * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF + * (128x96) or larger than VGA + */ + scale_h = calc_generic_scale(cam_subrect->width, pix->width); + scale_v = calc_generic_scale(cam_subrect->height, pix->height); + + dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); + + /* + * 4. Calculate client output window by applying combined scales to real + * input window. + */ + mf->width = scale_down(cam->rect.width, scale_h); + mf->height = scale_down(cam->rect.height, scale_v); +} + /* Similar to set_crop multistage iterative algorithm */ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) @@ -1460,18 +1511,17 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; __u32 pixfmt = pix->pixelformat; const struct soc_camera_format_xlate *xlate; - struct v4l2_crop cam_crop; - struct v4l2_rect *cam_rect = &cam_crop.c, cam_subrect, ceu_rect; - unsigned int scale_cam_h, scale_cam_v; + unsigned int ceu_sub_width, ceu_sub_height; u16 scale_v, scale_h; int ret; bool image_mode; enum v4l2_field field; + dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height); + switch (pix->field) { default: pix->field = V4L2_FIELD_NONE; @@ -1492,46 +1542,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* 1. Calculate current camera scales. */ - cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = client_g_rect(sd, cam_rect); - if (ret < 0) - return ret; - - ret = get_camera_scales(sd, cam_rect, &scale_cam_h, &scale_cam_v); - if (ret < 0) - return ret; - - dev_geo(dev, "1: camera scales %u:%u\n", scale_cam_h, scale_cam_v); - - /* - * 2. Calculate "effective" input crop (sensor subwindow) - CEU crop - * scaled back at current camera scales onto input window. - */ - ret = get_camera_subwin(icd, &cam_subrect, scale_cam_h, scale_cam_v); - if (ret < 0) - return ret; - - dev_geo(dev, "2: subwin %ux%u@%u:%u\n", - cam_subrect.width, cam_subrect.height, - cam_subrect.left, cam_subrect.top); - - /* - * 3. Calculate new combined scales from "effective" input window to - * requested user window. - */ - scale_h = calc_generic_scale(cam_subrect.width, pix->width); - scale_v = calc_generic_scale(cam_subrect.height, pix->height); - - dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); - - /* - * 4. Calculate camera output window by applying combined scales to real - * input window. - */ - mf.width = scale_down(cam_rect->width, scale_h); - mf.height = scale_down(cam_rect->height, scale_v); + /* 1.-4. Calculate client output geometry */ + calculate_client_output(icd, &f->fmt.pix, &mf); mf.field = pix->field; mf.colorspace = pix->colorspace; mf.code = xlate->code; @@ -1547,17 +1559,17 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, image_mode = false; } - dev_geo(dev, "4: camera output %ux%u\n", mf.width, mf.height); + dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); /* 5. - 9. */ - ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &mf, + ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height, image_mode && V4L2_FIELD_NONE == field); - dev_geo(dev, "5-9: client scale %d\n", ret); + dev_geo(dev, "5-9: client scale return %d\n", ret); /* Done with the camera. Now see if we can improve the result */ - dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + dev_geo(dev, "Camera %d fmt %ux%u, requested %ux%u\n", ret, mf.width, mf.height, pix->width, pix->height); if (ret < 0) return ret; @@ -1565,40 +1577,44 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, if (mf.code != xlate->code) return -EINVAL; + /* 9. Prepare CEU crop */ + cam->width = mf.width; + cam->height = mf.height; + /* 10. Use CEU scaling to scale to the requested user window. */ /* We cannot scale up */ - if (pix->width > mf.width) - pix->width = mf.width; - if (pix->width > ceu_rect.width) - pix->width = ceu_rect.width; + if (pix->width > ceu_sub_width) + ceu_sub_width = pix->width; - if (pix->height > mf.height) - pix->height = mf.height; - if (pix->height > ceu_rect.height) - pix->height = ceu_rect.height; + if (pix->height > ceu_sub_height) + ceu_sub_height = pix->height; pix->colorspace = mf.colorspace; if (image_mode) { /* Scale pix->{width x height} down to width x height */ - scale_h = calc_scale(ceu_rect.width, &pix->width); - scale_v = calc_scale(ceu_rect.height, &pix->height); - - pcdev->cflcr = scale_h | (scale_v << 16); + scale_h = calc_scale(ceu_sub_width, &pix->width); + scale_v = calc_scale(ceu_sub_height, &pix->height); } else { - pix->width = ceu_rect.width; - pix->height = ceu_rect.height; - scale_h = scale_v = 0; - pcdev->cflcr = 0; + pix->width = ceu_sub_width; + pix->height = ceu_sub_height; + scale_h = 0; + scale_v = 0; } + pcdev->cflcr = scale_h | (scale_v << 16); + + /* + * We have calculated CFLCR, the actual configuration will be performed + * in sh_mobile_ceu_set_bus_param() + */ + dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", - ceu_rect.width, scale_h, pix->width, - ceu_rect.height, scale_v, pix->height); + ceu_sub_width, scale_h, pix->width, + ceu_sub_height, scale_v, pix->height); cam->code = xlate->code; - cam->ceu_rect = ceu_rect; icd->current_fmt = xlate; pcdev->field = field; @@ -1820,6 +1836,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .remove = sh_mobile_ceu_remove_device, .get_formats = sh_mobile_ceu_get_formats, .put_formats = sh_mobile_ceu_put_formats, + .get_crop = sh_mobile_ceu_get_crop, .set_crop = sh_mobile_ceu_set_crop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c new file mode 100644 index 00000000000..f5b892a2a8e --- /dev/null +++ b/drivers/media/video/sh_vou.c @@ -0,0 +1,1476 @@ +/* + * SuperH Video Output Unit (VOU) driver + * + * 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/dma-mapping.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/version.h> +#include <linux/videodev2.h> + +#include <media/sh_vou.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mediabus.h> +#include <media/videobuf-dma-contig.h> + +/* Mirror addresses are not available for all registers */ +#define VOUER 0 +#define VOUCR 4 +#define VOUSTR 8 +#define VOUVCR 0xc +#define VOUISR 0x10 +#define VOUBCR 0x14 +#define VOUDPR 0x18 +#define VOUDSR 0x1c +#define VOUVPR 0x20 +#define VOUIR 0x24 +#define VOUSRR 0x28 +#define VOUMSR 0x2c +#define VOUHIR 0x30 +#define VOUDFR 0x34 +#define VOUAD1R 0x38 +#define VOUAD2R 0x3c +#define VOUAIR 0x40 +#define VOUSWR 0x44 +#define VOURCR 0x48 +#define VOURPR 0x50 + +enum sh_vou_status { + SH_VOU_IDLE, + SH_VOU_INITIALISING, + SH_VOU_RUNNING, +}; + +#define VOU_MAX_IMAGE_WIDTH 720 +#define VOU_MAX_IMAGE_HEIGHT 480 + +struct sh_vou_device { + struct v4l2_device v4l2_dev; + struct video_device *vdev; + atomic_t use_count; + struct sh_vou_pdata *pdata; + spinlock_t lock; + void __iomem *base; + /* State information */ + struct v4l2_pix_format pix; + struct v4l2_rect rect; + struct list_head queue; + v4l2_std_id std; + int pix_idx; + struct videobuf_buffer *active; + enum sh_vou_status status; +}; + +struct sh_vou_file { + struct videobuf_queue vbq; +}; + +/* Register access routines for sides A, B and mirror addresses */ +static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value) +{ + __raw_writel(value, vou_dev->base + reg); +} + +static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value) +{ + __raw_writel(value, vou_dev->base + reg); + __raw_writel(value, vou_dev->base + reg + 0x1000); +} + +static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value) +{ + __raw_writel(value, vou_dev->base + reg + 0x2000); +} + +static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg) +{ + return __raw_readl(vou_dev->base + reg); +} + +static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value, u32 mask) +{ + u32 old = __raw_readl(vou_dev->base + reg); + + value = (value & mask) | (old & ~mask); + __raw_writel(value, vou_dev->base + reg); +} + +static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value, u32 mask) +{ + sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask); +} + +static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value, u32 mask) +{ + sh_vou_reg_a_set(vou_dev, reg, value, mask); + sh_vou_reg_b_set(vou_dev, reg, value, mask); +} + +struct sh_vou_fmt { + u32 pfmt; + char *desc; + unsigned char bpp; + unsigned char rgb; + unsigned char yf; + unsigned char pkf; +}; + +/* Further pixel formats can be added */ +static struct sh_vou_fmt vou_fmt[] = { + { + .pfmt = V4L2_PIX_FMT_NV12, + .bpp = 12, + .desc = "YVU420 planar", + .yf = 0, + .rgb = 0, + }, + { + .pfmt = V4L2_PIX_FMT_NV16, + .bpp = 16, + .desc = "YVYU planar", + .yf = 1, + .rgb = 0, + }, + { + .pfmt = V4L2_PIX_FMT_RGB24, + .bpp = 24, + .desc = "RGB24", + .pkf = 2, + .rgb = 1, + }, + { + .pfmt = V4L2_PIX_FMT_RGB565, + .bpp = 16, + .desc = "RGB565", + .pkf = 3, + .rgb = 1, + }, + { + .pfmt = V4L2_PIX_FMT_RGB565X, + .bpp = 16, + .desc = "RGB565 byteswapped", + .pkf = 3, + .rgb = 1, + }, +}; + +static void sh_vou_schedule_next(struct sh_vou_device *vou_dev, + struct videobuf_buffer *vb) +{ + dma_addr_t addr1, addr2; + + addr1 = videobuf_to_dma_contig(vb); + switch (vou_dev->pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height; + break; + default: + addr2 = 0; + } + + sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1); + sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2); +} + +static void sh_vou_stream_start(struct sh_vou_device *vou_dev, + struct videobuf_buffer *vb) +{ + unsigned int row_coeff; +#ifdef __LITTLE_ENDIAN + u32 dataswap = 7; +#else + u32 dataswap = 0; +#endif + + switch (vou_dev->pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + row_coeff = 1; + break; + case V4L2_PIX_FMT_RGB565: + dataswap ^= 1; + case V4L2_PIX_FMT_RGB565X: + row_coeff = 2; + break; + case V4L2_PIX_FMT_RGB24: + row_coeff = 3; + break; + } + + sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap); + sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff); + sh_vou_schedule_next(vou_dev, vb); +} + +static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + BUG_ON(in_interrupt()); + + /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */ + videobuf_waiton(vb, 0, 0); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/* Locking: caller holds vq->vb_lock mutex */ +static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width * + vou_dev->pix.height / 8; + + if (*count < 2) + *count = 2; + + /* Taking into account maximum frame size, *count will stay >= 2 */ + if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024) + *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size); + + dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size); + + return 0; +} + +/* Locking: caller holds vq->vb_lock mutex */ +static int sh_vou_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct v4l2_pix_format *pix = &vou_dev->pix; + int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; + int ret; + + dev_dbg(vq->dev, "%s()\n", __func__); + + if (vb->width != pix->width || + vb->height != pix->height || + vb->field != pix->field) { + vb->width = pix->width; + vb->height = pix->height; + vb->field = field; + if (vb->state != VIDEOBUF_NEEDS_INIT) + free_buffer(vq, vb); + } + + vb->size = vb->height * bytes_per_line; + if (vb->baddr && vb->bsize < vb->size) { + /* User buffer too small */ + dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n", + vb->bsize, vb->baddr); + return -EINVAL; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret < 0) { + dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n", + vb->memory, ret); + return ret; + } + vb->state = VIDEOBUF_PREPARED; + } + + dev_dbg(vq->dev, + "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n", + __func__, vou_dev->pix_idx, bytes_per_line, + videobuf_to_dma_contig(vb), vb->memory, vb->state); + + return 0; +} + +/* Locking: caller holds vq->vb_lock mutex and vq->irqlock spinlock */ +static void sh_vou_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vq->dev, "%s()\n", __func__); + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &vou_dev->queue); + + if (vou_dev->status == SH_VOU_RUNNING) { + return; + } else if (!vou_dev->active) { + vou_dev->active = vb; + /* Start from side A: we use mirror addresses, so, set B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 1); + dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__, + sh_vou_reg_a_read(vou_dev, VOUSTR)); + sh_vou_schedule_next(vou_dev, vb); + /* Only activate VOU after the second buffer */ + } else if (vou_dev->active->queue.next == &vb->queue) { + /* Second buffer - initialise register side B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 0); + sh_vou_stream_start(vou_dev, vb); + + /* Register side switching with frame VSYNC */ + sh_vou_reg_a_write(vou_dev, VOURCR, 5); + dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__, + sh_vou_reg_a_read(vou_dev, VOUSTR)); + + /* Enable End-of-Frame (VSYNC) interrupts */ + sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); + /* Two buffers on the queue - activate the hardware */ + + vou_dev->status = SH_VOU_RUNNING; + sh_vou_reg_a_write(vou_dev, VOUER, 0x107); + } +} + +static void sh_vou_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + unsigned long flags; + + dev_dbg(vq->dev, "%s()\n", __func__); + + spin_lock_irqsave(&vou_dev->lock, flags); + + if (vou_dev->active == vb) { + /* disable output */ + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); + /* ...but the current frame will complete */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + vou_dev->active = NULL; + } + + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) { + vb->state = VIDEOBUF_ERROR; + list_del(&vb->queue); + } + + spin_unlock_irqrestore(&vou_dev->lock, flags); + + free_buffer(vq, vb); +} + +static struct videobuf_queue_ops sh_vou_video_qops = { + .buf_setup = sh_vou_buf_setup, + .buf_prepare = sh_vou_buf_prepare, + .buf_queue = sh_vou_buf_queue, + .buf_release = sh_vou_buf_release, +}; + +/* Video IOCTLs */ +static int sh_vou_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); + cap->version = KERNEL_VERSION(0, 1, 0); + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + return 0; +} + +/* Enumerate formats, that the device can accept from the user */ +static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct sh_vou_file *vou_file = priv; + + if (fmt->index >= ARRAY_SIZE(vou_fmt)) + return -EINVAL; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + strlcpy(fmt->description, vou_fmt[fmt->index].desc, + sizeof(fmt->description)); + fmt->pixelformat = vou_fmt[fmt->index].pfmt; + + return 0; +} + +static int sh_vou_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmt->fmt.pix = vou_dev->pix; + + return 0; +} + +static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4}; +static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1}; +static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3}; +static const unsigned char vou_scale_v_num[] = {1, 2, 4}; +static const unsigned char vou_scale_v_den[] = {1, 1, 1}; +static const unsigned char vou_scale_v_fld[] = {0, 1}; + +static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev, + int pix_idx, int w_idx, int h_idx) +{ + struct sh_vou_fmt *fmt = vou_fmt + pix_idx; + unsigned int black_left, black_top, width_max, height_max, + frame_in_height, frame_out_height, frame_out_top; + struct v4l2_rect *rect = &vou_dev->rect; + struct v4l2_pix_format *pix = &vou_dev->pix; + u32 vouvcr = 0, dsr_h, dsr_v; + + if (vou_dev->std & V4L2_STD_525_60) { + width_max = 858; + height_max = 262; + } else { + width_max = 864; + height_max = 312; + } + + frame_in_height = pix->height / 2; + frame_out_height = rect->height / 2; + frame_out_top = rect->top / 2; + + /* + * Cropping scheme: max useful image is 720x480, and the total video + * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts + * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock, + * of which the first 33 / 25 clocks HSYNC must be held active. This + * has to be configured in CR[HW]. 1 pixel equals 2 clock periods. + * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives + * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area, + * beyond DSR, specified on the left and top by the VPR register "black + * pixels" and out-of-image area (DPR) "background pixels." We fix VPR + * at 138 / 144 : 20, because that's the HSYNC timing, that our first + * client requires, and that's exactly what leaves us 720 pixels for the + * image; we leave VPR[VVP] at default 20 for now, because the client + * doesn't seem to have any special requirements for it. Otherwise we + * could also set it to max - 240 = 22 / 72. Thus VPR depends only on + * the selected standard, and DPR and DSR are selected according to + * cropping. Q: how does the client detect the first valid line? Does + * HSYNC stay inactive during invalid (black) lines? + */ + black_left = width_max - VOU_MAX_IMAGE_WIDTH; + black_top = 20; + + dsr_h = rect->width + rect->left; + dsr_v = frame_out_height + frame_out_top; + + dev_dbg(vou_dev->v4l2_dev.dev, + "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n", + pix->width, frame_in_height, black_left, black_top, + rect->left, frame_out_top, dsr_h, dsr_v); + + /* VOUISR height - half of a frame height in frame mode */ + sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height); + sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top); + sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top); + sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v); + + /* + * if necessary, we could set VOUHIR to + * max(black_left + dsr_h, width_max) here + */ + + if (w_idx) + vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4); + if (h_idx) + vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1]; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s: scaling 0x%x\n", fmt->desc, vouvcr); + + /* To produce a colour bar for testing set bit 23 of VOUVCR */ + sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr); + sh_vou_reg_ab_write(vou_dev, VOUDFR, + fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16)); +} + +struct sh_vou_geometry { + struct v4l2_rect output; + unsigned int in_width; + unsigned int in_height; + int scale_idx_h; + int scale_idx_v; +}; + +/* + * Find input geometry, that we can use to produce output, closest to the + * requested rectangle, using VOU scaling + */ +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; + int i, idx = 0; + + if (std & V4L2_STD_525_60) { + width_max = 858; + height_max = 262; + } else { + width_max = 864; + height_max = 312; + } + + /* 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); + + /* Select scales to come as close as possible to the output image */ + for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { + unsigned int err; + unsigned int found = geo->output.width * vou_scale_h_den[i] / + vou_scale_h_num[i]; + + if (found > VOU_MAX_IMAGE_WIDTH) + /* scales increase */ + break; + + err = abs(found - geo->in_width); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->in_width = best; + geo->scale_idx_h = idx; + + best_err = UINT_MAX; + + /* This loop can be replaced with one division */ + for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) { + unsigned int err; + unsigned int found = geo->output.height * vou_scale_v_den[i] / + vou_scale_v_num[i]; + + if (found > VOU_MAX_IMAGE_HEIGHT) + /* scales increase */ + break; + + err = abs(found - geo->in_height); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->in_height = best; + geo->scale_idx_v = idx; +} + +/* + * Find output geometry, that we can produce, using VOU scaling, closest to + * the requested rectangle + */ +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; + int i, idx; + + if (std & V4L2_STD_525_60) { + width_max = 858; + height_max = 262 * 2; + } else { + width_max = 864; + height_max = 312 * 2; + } + + /* Select scales to come as close as possible to the output image */ + for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) { + unsigned int err; + unsigned int found = geo->in_width * vou_scale_h_num[i] / + vou_scale_h_den[i]; + + if (found > VOU_MAX_IMAGE_WIDTH) + /* scales increase */ + break; + + err = abs(found - geo->output.width); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->output.width = best; + geo->scale_idx_h = idx; + if (geo->output.left + best > width_max) + geo->output.left = width_max - best; + + pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width, + vou_scale_h_num[idx], vou_scale_h_den[idx], best); + + best_err = UINT_MAX; + + /* This loop can be replaced with one division */ + for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) { + unsigned int err; + unsigned int found = geo->in_height * vou_scale_v_num[i] / + vou_scale_v_den[i]; + + if (found > VOU_MAX_IMAGE_HEIGHT) + /* scales increase */ + break; + + err = abs(found - geo->output.height); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->output.height = best; + geo->scale_idx_v = idx; + if (geo->output.top + best > height_max) + geo->output.top = height_max - best; + + pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height, + vou_scale_v_num[idx], vou_scale_v_den[idx], best); +} + +static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + 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; + 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, + .field = V4L2_FIELD_INTERLACED, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }; + int ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, + vou_dev->rect.width, vou_dev->rect.height, + pix->width, pix->height); + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + pix->field != V4L2_FIELD_NONE) + return -EINVAL; + + for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) + if (vou_fmt[pix_idx].pfmt == pix->pixelformat) + break; + + if (pix_idx == ARRAY_SIZE(vou_fmt)) + return -EINVAL; + + /* 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); + + geo.in_width = pix->width; + geo.in_height = pix->height; + geo.output = vou_dev->rect; + + vou_adjust_output(&geo, vou_dev->std); + + mbfmt.width = geo.output.width; + mbfmt.height = geo.output.height; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_mbus_fmt, &mbfmt); + /* Must be implemented, so, don't check for -ENOIOCTLCMD */ + if (ret < 0) + return ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, + geo.output.width, geo.output.height, mbfmt.width, mbfmt.height); + + /* 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) + return -EIO; + + if (mbfmt.width != geo.output.width || + mbfmt.height != geo.output.height) { + geo.output.width = mbfmt.width; + geo.output.height = mbfmt.height; + + vou_adjust_input(&geo, vou_dev->std); + } + + /* We tried to preserve output rectangle, but it could have changed */ + vou_dev->rect = geo.output; + pix->width = geo.in_width; + pix->height = geo.in_height; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__, + pix->width, pix->height); + + vou_dev->pix_idx = pix_idx; + + vou_dev->pix = *pix; + + sh_vou_configure_geometry(vou_dev, pix_idx, + geo.scale_idx_h, geo.scale_idx_v); + + return 0; +} + +static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct sh_vou_file *vou_file = priv; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int i; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + pix->field = V4L2_FIELD_NONE; + + v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1, + &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + + for (i = 0; ARRAY_SIZE(vou_fmt); i++) + if (vou_fmt[i].pfmt == pix->pixelformat) + return 0; + + pix->pixelformat = vou_fmt[0].pfmt; + + return 0; +} + +static int sh_vou_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + return videobuf_reqbufs(&vou_file->vbq, req); +} + +static int sh_vou_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_querybuf(&vou_file->vbq, b); +} + +static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_qbuf(&vou_file->vbq, b); +} + +static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK); +} + +static int sh_vou_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = priv; + int ret; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, + video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* This calls our .buf_queue() (== sh_vou_buf_queue) */ + return videobuf_streamon(&vou_file->vbq); +} + +static int sh_vou_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + /* + * This calls buf_release from host driver's videobuf_queue_ops for all + * remaining buffers. When the last buffer is freed, stop streaming + */ + videobuf_streamoff(&vou_file->vbq); + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0); + + return 0; +} + +static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) +{ + switch (bus_fmt) { + default: + pr_warning("%s(): Invalid bus-format code %d, using default 8-bit\n", + __func__, bus_fmt); + case SH_VOU_BUS_8BIT: + return 1; + case SH_VOU_BUS_16BIT: + return 0; + case SH_VOU_BUS_BT656: + return 3; + } +} + +static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + int ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id); + + if (*std_id & ~vdev->tvnorms) + return -EINVAL; + + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_std_output, *std_id); + /* Shall we continue, if the subdev doesn't support .s_std_output()? */ + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + if (*std_id & V4L2_STD_525_60) + sh_vou_reg_ab_set(vou_dev, VOUCR, + sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); + else + sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29); + + vou_dev->std = *std_id; + + return 0; +} + +static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + *std = vou_dev->std; + + return 0; +} + +static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->c = vou_dev->rect; + + return 0; +} + +/* Assume a dull encoder, do all the work ourselves. */ +static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct v4l2_rect *rect = &a->c; + struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; + struct v4l2_pix_format *pix = &vou_dev->pix; + struct sh_vou_geometry geo; + struct v4l2_mbus_framefmt mbfmt = { + /* Revisit: is this the correct code? */ + .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .field = V4L2_FIELD_INTERLACED, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }; + int ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__, + rect->width, rect->height, rect->left, rect->top); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1, + &rect->height, 0, VOU_MAX_IMAGE_HEIGHT, 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; + + geo.output = *rect; + geo.in_width = pix->width; + geo.in_height = pix->height; + + /* Configure the encoder one-to-one, position at 0, ignore errors */ + sd_crop.c.width = geo.output.width; + sd_crop.c.height = geo.output.height; + /* + * We first issue a S_CROP, so that the subsequent S_FMT delivers the + * final encoder configuration. + */ + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_crop, &sd_crop); + mbfmt.width = geo.output.width; + mbfmt.height = geo.output.height; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_mbus_fmt, &mbfmt); + /* Must be implemented, so, don't check for -ENOIOCTLCMD */ + if (ret < 0) + return ret; + + /* 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) + return -EIO; + + geo.output.width = mbfmt.width; + geo.output.height = mbfmt.height; + + /* + * No down-scaling. According to the API, current call has precedence: + * http://v4l2spec.bytesex.org/spec/x1904.htm#AEN1954 paragraph two. + */ + vou_adjust_input(&geo, vou_dev->std); + + /* We tried to preserve output rectangle, but it could have changed */ + vou_dev->rect = geo.output; + pix->width = geo.in_width; + pix->height = geo.in_height; + + sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx, + geo.scale_idx_h, geo.scale_idx_v); + + return 0; +} + +/* + * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle + * is the initial register values, height takes the interlaced format into + * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can + * actually only meaningfully contain values <= 720 and <= 240 respectively, and + * not <= 864 and <= 312. + */ +static int sh_vou_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *a) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = VOU_MAX_IMAGE_WIDTH; + a->bounds.height = VOU_MAX_IMAGE_HEIGHT; + /* Default = max, set VOUDPR = 0, which is not hardware default */ + a->defrect.left = 0; + a->defrect.top = 0; + a->defrect.width = VOU_MAX_IMAGE_WIDTH; + a->defrect.height = VOU_MAX_IMAGE_HEIGHT; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static irqreturn_t sh_vou_isr(int irq, void *dev_id) +{ + struct sh_vou_device *vou_dev = dev_id; + static unsigned long j; + struct videobuf_buffer *vb; + static int cnt; + static int side; + u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked; + u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR); + + if (!(irq_status & 0x300)) { + if (printk_timed_ratelimit(&j, 500)) + dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n", + irq_status); + return IRQ_NONE; + } + + spin_lock(&vou_dev->lock); + if (!vou_dev->active || list_empty(&vou_dev->queue)) { + if (printk_timed_ratelimit(&j, 500)) + dev_warn(vou_dev->v4l2_dev.dev, + "IRQ without active buffer: %x!\n", irq_status); + /* Just ack: buf_release will disable further interrupts */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300); + spin_unlock(&vou_dev->lock); + return IRQ_HANDLED; + } + + masked = ~(0x300 & irq_status) & irq_status & 0x30304; + dev_dbg(vou_dev->v4l2_dev.dev, + "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n", + irq_status, masked, vou_status, cnt); + + cnt++; + side = vou_status & 0x10000; + + /* Clear only set interrupts */ + sh_vou_reg_a_write(vou_dev, VOUIR, masked); + + vb = vou_dev->active; + list_del(&vb->queue); + + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&vou_dev->queue)) { + /* Stop VOU */ + dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n", + __func__, cnt); + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); + vou_dev->active = NULL; + vou_dev->status = SH_VOU_INITIALISING; + /* Disable End-of-Frame (VSYNC) interrupts */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + spin_unlock(&vou_dev->lock); + return IRQ_HANDLED; + } + + vou_dev->active = list_entry(vou_dev->queue.next, + struct videobuf_buffer, queue); + + if (vou_dev->active->queue.next != &vou_dev->queue) { + struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next, + struct videobuf_buffer, queue); + sh_vou_schedule_next(vou_dev, new); + } + + spin_unlock(&vou_dev->lock); + + return IRQ_HANDLED; +} + +static int sh_vou_hw_init(struct sh_vou_device *vou_dev) +{ + struct sh_vou_pdata *pdata = vou_dev->pdata; + u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29; + int i = 100; + + /* Disable all IRQs */ + sh_vou_reg_a_write(vou_dev, VOUIR, 0); + + /* Reset VOU interfaces - registers unaffected */ + sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101); + while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101)) + udelay(1); + + if (!i) + return -ETIMEDOUT; + + dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i); + + if (pdata->flags & SH_VOU_PCLK_FALLING) + voucr |= 1 << 28; + if (pdata->flags & SH_VOU_HSYNC_LOW) + voucr |= 1 << 27; + if (pdata->flags & SH_VOU_VSYNC_LOW) + voucr |= 1 << 26; + sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000); + + /* Manual register side switching at first */ + sh_vou_reg_a_write(vou_dev, VOURCR, 4); + /* Default - fixed HSYNC length, can be made configurable is required */ + sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000); + + return 0; +} + +/* File operations */ +static int sh_vou_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file), + GFP_KERNEL); + + if (!vou_file) + return -ENOMEM; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + file->private_data = vou_file; + + if (atomic_inc_return(&vou_dev->use_count) == 1) { + int ret; + /* First open */ + vou_dev->status = SH_VOU_INITIALISING; + pm_runtime_get_sync(vdev->v4l2_dev->dev); + ret = sh_vou_hw_init(vou_dev); + if (ret < 0) { + atomic_dec(&vou_dev->use_count); + pm_runtime_put(vdev->v4l2_dev->dev); + vou_dev->status = SH_VOU_IDLE; + return ret; + } + } + + videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops, + vou_dev->v4l2_dev.dev, &vou_dev->lock, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vdev); + + return 0; +} + +static int sh_vou_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = file->private_data; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + if (!atomic_dec_return(&vou_dev->use_count)) { + /* Last close */ + vou_dev->status = SH_VOU_IDLE; + sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); + pm_runtime_put(vdev->v4l2_dev->dev); + } + + file->private_data = NULL; + kfree(vou_file); + + return 0; +} + +static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sh_vou_file *vou_file = file->private_data; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_mmap_mapper(&vou_file->vbq, vma); +} + +static unsigned int sh_vou_poll(struct file *file, poll_table *wait) +{ + struct sh_vou_file *vou_file = file->private_data; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_poll_stream(file, &vou_file->vbq, wait); +} + +static int sh_vou_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *id) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sh_vou_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg); +} + +static int sh_vou_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg); +} +#endif + +/* sh_vou display ioctl operations */ +static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { + .vidioc_querycap = sh_vou_querycap, + .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out, + .vidioc_reqbufs = sh_vou_reqbufs, + .vidioc_querybuf = sh_vou_querybuf, + .vidioc_qbuf = sh_vou_qbuf, + .vidioc_dqbuf = sh_vou_dqbuf, + .vidioc_streamon = sh_vou_streamon, + .vidioc_streamoff = sh_vou_streamoff, + .vidioc_s_std = sh_vou_s_std, + .vidioc_g_std = sh_vou_g_std, + .vidioc_cropcap = sh_vou_cropcap, + .vidioc_g_crop = sh_vou_g_crop, + .vidioc_s_crop = sh_vou_s_crop, + .vidioc_g_chip_ident = sh_vou_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = sh_vou_g_register, + .vidioc_s_register = sh_vou_s_register, +#endif +}; + +static const struct v4l2_file_operations sh_vou_fops = { + .owner = THIS_MODULE, + .open = sh_vou_open, + .release = sh_vou_release, + .ioctl = video_ioctl2, + .mmap = sh_vou_mmap, + .poll = sh_vou_poll, +}; + +static const struct video_device sh_vou_video_template = { + .name = "sh_vou", + .fops = &sh_vou_fops, + .ioctl_ops = &sh_vou_ioctl_ops, + .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */ + .current_norm = V4L2_STD_NTSC_M, +}; + +static int __devinit sh_vou_probe(struct platform_device *pdev) +{ + struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data; + struct v4l2_rect *rect; + struct v4l2_pix_format *pix; + struct i2c_adapter *i2c_adap; + struct video_device *vdev; + struct sh_vou_device *vou_dev; + struct resource *reg_res, *region; + struct v4l2_subdev *subdev; + int irq, ret; + + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!vou_pdata || !reg_res || irq <= 0) { + dev_err(&pdev->dev, "Insufficient VOU platform information.\n"); + return -ENODEV; + } + + vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL); + if (!vou_dev) + return -ENOMEM; + + INIT_LIST_HEAD(&vou_dev->queue); + spin_lock_init(&vou_dev->lock); + atomic_set(&vou_dev->use_count, 0); + vou_dev->pdata = vou_pdata; + vou_dev->status = SH_VOU_IDLE; + + rect = &vou_dev->rect; + pix = &vou_dev->pix; + + /* Fill in defaults */ + vou_dev->std = sh_vou_video_template.current_norm; + rect->left = 0; + rect->top = 0; + rect->width = VOU_MAX_IMAGE_WIDTH; + rect->height = VOU_MAX_IMAGE_HEIGHT; + pix->width = VOU_MAX_IMAGE_WIDTH; + pix->height = VOU_MAX_IMAGE_HEIGHT; + 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->colorspace = V4L2_COLORSPACE_SMPTE170M; + + region = request_mem_region(reg_res->start, resource_size(reg_res), + pdev->name); + if (!region) { + dev_err(&pdev->dev, "VOU region already claimed\n"); + ret = -EBUSY; + goto ereqmemreg; + } + + vou_dev->base = ioremap(reg_res->start, resource_size(reg_res)); + if (!vou_dev->base) { + ret = -ENOMEM; + goto emap; + } + + ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev); + if (ret < 0) + goto ereqirq; + + ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error registering v4l2 device\n"); + goto ev4l2devreg; + } + + /* Allocate memory for video device */ + vdev = video_device_alloc(); + if (vdev == NULL) { + ret = -ENOMEM; + goto evdevalloc; + } + + *vdev = sh_vou_video_template; + if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT) + vdev->tvnorms |= V4L2_STD_PAL; + vdev->v4l2_dev = &vou_dev->v4l2_dev; + vdev->release = video_device_release; + + vou_dev->vdev = vdev; + video_set_drvdata(vdev, vou_dev); + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap); + if (!i2c_adap) { + ret = -ENODEV; + goto ei2cgadap; + } + + ret = sh_vou_hw_init(vou_dev); + if (ret < 0) + goto ereset; + + subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap, + vou_pdata->module_name, vou_pdata->board_info, NULL); + if (!subdev) { + ret = -ENOMEM; + goto ei2cnd; + } + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + goto evregdev; + + return 0; + +evregdev: +ei2cnd: +ereset: + i2c_put_adapter(i2c_adap); +ei2cgadap: + video_device_release(vdev); + pm_runtime_disable(&pdev->dev); +evdevalloc: + v4l2_device_unregister(&vou_dev->v4l2_dev); +ev4l2devreg: + free_irq(irq, vou_dev); +ereqirq: + iounmap(vou_dev->base); +emap: + release_mem_region(reg_res->start, resource_size(reg_res)); +ereqmemreg: + kfree(vou_dev); + return ret; +} + +static int __devexit sh_vou_remove(struct platform_device *pdev) +{ + int irq = platform_get_irq(pdev, 0); + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct sh_vou_device *vou_dev = container_of(v4l2_dev, + struct sh_vou_device, v4l2_dev); + struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct resource *reg_res; + + if (irq > 0) + free_irq(irq, vou_dev); + pm_runtime_disable(&pdev->dev); + video_unregister_device(vou_dev->vdev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&vou_dev->v4l2_dev); + iounmap(vou_dev->base); + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (reg_res) + release_mem_region(reg_res->start, resource_size(reg_res)); + kfree(vou_dev); + return 0; +} + +static struct platform_driver __refdata sh_vou = { + .remove = __devexit_p(sh_vou_remove), + .driver = { + .name = "sh-vou", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_vou_init(void) +{ + return platform_driver_probe(&sh_vou, sh_vou_probe); +} + +static void __exit sh_vou_exit(void) +{ + platform_driver_unregister(&sh_vou); +} + +module_init(sh_vou_init); +module_exit(sh_vou_exit); + +MODULE_DESCRIPTION("SuperH VOU driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sh-vou"); diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index cbf8087b286..28e19daadec 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -2295,7 +2295,7 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + 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; @@ -2305,7 +2305,9 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) 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; diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index cc40d6ba9f2..522ba3f4c28 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -40,12 +40,12 @@ struct sn9c102_device; static const struct usb_device_id sn9c102_id_table[] = { /* SN9C101 and SN9C102 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ @@ -53,13 +53,13 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ #endif @@ -74,7 +74,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, @@ -86,7 +86,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, @@ -97,7 +97,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, /* SN9C105 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, @@ -121,11 +121,11 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, #endif diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c index db243494893..2dce5c908c8 100644 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -255,7 +255,7 @@ int sn9c102_probe_hv7131d(struct sn9c102_device* cam) if (err || r0 < 0 || r1 < 0) return -EIO; - if (r0 != 0x00 || r1 != 0x04) + if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) return -ENODEV; sn9c102_attach_sensor(cam, &hv7131d); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a24174ddec4..db1ca0e90d7 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <linux/vmalloc.h> #include <media/soc_camera.h> @@ -388,6 +389,11 @@ static int soc_camera_open(struct file *file) goto eiciadd; } + pm_runtime_enable(&icd->vdev->dev); + ret = pm_runtime_resume(&icd->vdev->dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + /* * Try to configure with default parameters. Notice: this is the * very first open, so, we cannot race against other calls, @@ -409,10 +415,12 @@ static int soc_camera_open(struct file *file) return 0; /* - * First five errors are entered with the .video_lock held + * First four errors are entered with the .video_lock held * and use_count == 1 */ esfmt: + pm_runtime_disable(&icd->vdev->dev); +eresume: ici->ops->remove(icd); eiciadd: if (icl->power) @@ -437,7 +445,11 @@ static int soc_camera_close(struct file *file) if (!icd->use_count) { struct soc_camera_link *icl = to_soc_camera_link(icd); + pm_runtime_suspend(&icd->vdev->dev); + pm_runtime_disable(&icd->vdev->dev); + ici->ops->remove(icd); + if (icl->power) icl->power(icd->pdev, 0); } @@ -741,8 +753,7 @@ static int soc_camera_g_crop(struct file *file, void *fh, /* * According to the V4L2 API, drivers shall not update the struct v4l2_crop * argument with the actual geometry, instead, the user shall use G_CROP to - * retrieve it. However, we expect camera host and client drivers to update - * the argument, which we then use internally, but do not return to the user. + * retrieve it. */ static int soc_camera_s_crop(struct file *file, void *fh, struct v4l2_crop *a) @@ -1319,6 +1330,7 @@ static int video_dev_create(struct soc_camera_device *icd) */ static int soc_camera_video_start(struct soc_camera_device *icd) { + struct device_type *type = icd->vdev->dev.type; int ret; if (!icd->dev.parent) @@ -1335,6 +1347,9 @@ static int soc_camera_video_start(struct soc_camera_device *icd) return ret; } + /* Restore device type, possibly set by the subdevice driver */ + icd->vdev->dev.type = type; + return 0; } diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c index ebd9cb5bec7..edd78f8b1ba 100644 --- a/drivers/media/video/tlg2300/pd-dvb.c +++ b/drivers/media/video/tlg2300/pd-dvb.c @@ -97,15 +97,17 @@ open_out: return ret; } +#ifdef CONFIG_PM static void poseidon_fe_release(struct dvb_frontend *fe) { struct poseidon *pd = fe->demodulator_priv; -#ifdef CONFIG_PM pd->pm_suspend = NULL; pd->pm_resume = NULL; -#endif } +#else +#define poseidon_fe_release NULL +#endif static s32 poseidon_fe_sleep(struct dvb_frontend *fe) { diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index 2cf0ebf9f28..c267e0cfb54 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -24,7 +24,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -55,8 +54,8 @@ int debug_mode; module_param(debug_mode, int, 0644); MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); -const char *firmware_name = "tlg2300_firmware.bin"; -struct usb_driver poseidon_driver; +static const char *firmware_name = "tlg2300_firmware.bin"; +static struct usb_driver poseidon_driver; static LIST_HEAD(pd_device_list); /* @@ -501,7 +500,7 @@ static void poseidon_disconnect(struct usb_interface *interface) kref_put(&pd->kref, poseidon_delete); } -struct usb_driver poseidon_driver = { +static struct usb_driver poseidon_driver = { .name = "poseidon", .probe = poseidon_probe, .disconnect = poseidon_disconnect, diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c index 755766b1515..fae84c2a0c3 100644 --- a/drivers/media/video/tlg2300/pd-radio.c +++ b/drivers/media/video/tlg2300/pd-radio.c @@ -161,7 +161,8 @@ static const struct v4l2_file_operations poseidon_fm_fops = { .ioctl = video_ioctl2, }; -int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) { struct tuner_fm_sig_stat_s fm_stat = {}; int ret, status, count = 5; @@ -203,7 +204,8 @@ int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) return 0; } -int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp) +static int fm_get_freq(struct file *file, void *priv, + struct v4l2_frequency *argp) { struct poseidon *p = file->private_data; @@ -246,7 +248,8 @@ error: return ret; } -int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp) +static int fm_set_freq(struct file *file, void *priv, + struct v4l2_frequency *argp) { struct poseidon *p = file->private_data; @@ -258,13 +261,13 @@ int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp) return set_frequency(p, argp->frequency); } -int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, +static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *arg) { return 0; } -int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, +static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, struct v4l2_ext_controls *ctrls) { struct poseidon *p = file->private_data; @@ -285,7 +288,7 @@ int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, return 0; } -int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, +static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, struct v4l2_ext_controls *ctrls) { int i; @@ -312,13 +315,13 @@ int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, return 0; } -int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, +static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { return 0; } -int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, +static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *ctrl) { if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) @@ -337,7 +340,7 @@ int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, return -EINVAL; } -int tlg_fm_vidioc_querymenu(struct file *file, void *fh, +static int tlg_fm_vidioc_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) { return v4l2_ctrl_query_menu(qmenu, NULL, NULL); diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index cf8f18c007e..c750fd115ec 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -12,11 +12,13 @@ #include "pd-common.h" #include "vendorcmds.h" +#ifdef CONFIG_PM static int pm_video_suspend(struct poseidon *pd); static int pm_video_resume(struct poseidon *pd); +#endif static void iso_bubble_handler(struct work_struct *w); -int usb_transfer_mode; +static int usb_transfer_mode; module_param(usb_transfer_mode, int, 0644); MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); @@ -617,7 +619,7 @@ static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return 0; } -int fire_all_urb(struct video_data *video) +static int fire_all_urb(struct video_data *video) { int i, ret; @@ -877,7 +879,7 @@ out: return ret; } -int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) { struct front_face *front = fh; logs(front); @@ -1020,7 +1022,7 @@ static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) { a->index = 0; a->capability = V4L2_AUDCAP_STEREO; @@ -1029,7 +1031,7 @@ int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { return (0 == a->index) ? 0 : -EINVAL; } @@ -1189,7 +1191,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) } /* Just stop the URBs, do not free the URBs */ -int usb_transfer_stop(struct video_data *video) +static int usb_transfer_stop(struct video_data *video) { if (video->is_streaming) { int i; @@ -1518,13 +1520,13 @@ static int pd_video_mmap(struct file *file, struct vm_area_struct *vma) return videobuf_mmap_mapper(&front->q, vma); } -unsigned int pd_video_poll(struct file *file, poll_table *table) +static unsigned int pd_video_poll(struct file *file, poll_table *table) { struct front_face *front = file->private_data; return videobuf_poll_stream(file, &front->q, table); } -ssize_t pd_video_read(struct file *file, char __user *buffer, +static ssize_t pd_video_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct front_face *front = file->private_data; diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index e4815a1806e..e826114b7fb 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -79,6 +79,8 @@ struct tvp514x_std_info { }; static struct tvp514x_reg tvp514x_reg_list_default[0x40]; + +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); /** * struct tvp514x_decoder - TVP5146/47 decoder object * @sd: Subdevice Slave handle @@ -644,6 +646,17 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, /* Index out of bound */ return -EINVAL; + /* + * For the sequence streamon -> streamoff and again s_input + * it fails to lock the signal, since streamoff puts TVP514x + * into power off state which leads to failure in sub-sequent s_input. + * + * So power up the TVP514x device here, since it is important to lock + * the signal at this stage. + */ + if (!decoder->streaming) + tvp514x_s_stream(sd, 1); + input_sel = input; output_sel = output; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 908ffb68e92..47f0582d50a 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -891,29 +891,26 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd, return 0; } -static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + /* this is for capturing 36 raw vbi lines + if there's a way to cut off the beginning 2 vbi lines + with the tvp5150 then the vbi line count could be lowered + to 17 lines/field again, although I couldn't find a register + which could do that cropping */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) + tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + if (fmt->count[0] == 18 && fmt->count[1] == 18) { + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + } + return 0; +} + +static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { - struct v4l2_sliced_vbi_format *svbi; int i; - /* raw vbi */ - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ - if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); - if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) { - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); - } - return 0; - } - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; if (svbi->service_set != 0) { for (i = 0; i <= 23; i++) { svbi->service_lines[1][i] = 0; @@ -937,14 +934,21 @@ static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } -static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct v4l2_sliced_vbi_format *svbi; - int i, mask = 0; + switch (fmt->type) { + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return tvp5150_s_sliced_fmt(sd, &fmt->fmt.sliced); - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + default: return -EINVAL; - svbi = &fmt->fmt.sliced; + } +} + +static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + int i, mask = 0; + memset(svbi, 0, sizeof(*svbi)); for (i = 0; i <= 23; i++) { @@ -956,6 +960,12 @@ static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return tvp5150_g_sliced_fmt(sd, &fmt->fmt.sliced); +} static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) @@ -1046,13 +1056,20 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .s_routing = tvp5150_s_routing, .g_fmt = tvp5150_g_fmt, .s_fmt = tvp5150_s_fmt, +}; + +static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, + .g_sliced_fmt = tvp5150_g_sliced_fmt, + .s_sliced_fmt = tvp5150_s_sliced_fmt, + .s_raw_fmt = tvp5150_s_raw_fmt, }; static const struct v4l2_subdev_ops tvp5150_ops = { .core = &tvp5150_core_ops, .tuner = &tvp5150_tuner_ops, .video = &tvp5150_video_ops, + .vbi = &tvp5150_vbi_ops, }; diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 4a69bcc738f..8085ac39244 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -458,7 +458,7 @@ static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) /* * tvp7002_read - Read a value from a register in an TVP7002 * @sd: ptr to v4l2_subdev struct - * @reg: TVP7002 register address + * @addr: TVP7002 register address * @dst: pointer to 8-bit destination * * Returns value read if successful, or non-zero (-1) otherwise. @@ -488,7 +488,7 @@ static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) * @sd: pointer to standard V4L2 sub-device structure * @reg: destination register * @val: value to be read - * @error: pointer to error value + * @err: pointer to error value * * Read a value in a register and save error value in pointer. * Also update the register table if successful @@ -535,7 +535,7 @@ static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) * @sd: pointer to standard V4L2 sub-device structure * @reg: destination register * @val: value to be written - * @error: pointer to error value + * @err: pointer to error value * * Write a value in a register and save error value in pointer. * Also update the register table if successful @@ -596,7 +596,7 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd, /* * tvp7002_s_dv_preset() - Set digital video preset * @sd: ptr to v4l2_subdev struct - * @std: ptr to v4l2_dv_preset struct + * @dv_preset: ptr to v4l2_dv_preset struct * * Set the digital video preset for a TVP7002 decoder device. * Returns zero when successful or -EINVAL if register access fails. @@ -676,7 +676,7 @@ static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) /* * tvp7002_queryctrl() - Query a control * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to v4l2_queryctrl struct + * @qc: ptr to v4l2_queryctrl struct * * Query a control of a TVP7002 decoder device. * Returns zero when successful or -EINVAL if register read fails. @@ -776,7 +776,7 @@ static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) /* * tvp7002_query_dv_preset() - query DV preset * @sd: pointer to standard V4L2 sub-device structure - * @std_id: standard V4L2 v4l2_dv_preset + * @qpreset: standard V4L2 v4l2_dv_preset structure * * Returns the current DV preset by TVP7002. If no active input is * detected, returns -EINVAL @@ -785,7 +785,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, struct v4l2_dv_preset *qpreset) { const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct v4l2_dv_enum_preset e_preset; struct tvp7002 *device; u8 progressive; u32 lpfr; @@ -828,20 +827,18 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, } if (index == NUM_PRESETS) { - v4l2_err(sd, "querystd error, lpf = %x, cpl = %x\n", + v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", lpfr, cpln); - return -EINVAL; + /* Could not detect a signal, so return the 'invalid' preset */ + qpreset->preset = V4L2_DV_INVALID; + return 0; } - if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) - return -EINVAL; - /* Set values in found preset */ qpreset->preset = presets->preset; /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "Current preset: %d %d", - e_preset.width, e_preset.height); + v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset); return 0; } @@ -849,7 +846,7 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, /* * tvp7002_g_register() - Get the value of a register * @sd: ptr to v4l2_subdev struct - * @vreg: ptr to v4l2_dbg_register struct + * @reg: ptr to v4l2_dbg_register struct * * Get the value of a TVP7002 decoder device register. * Returns zero when successful, -EINVAL if register read fails or @@ -876,7 +873,7 @@ static int tvp7002_g_register(struct v4l2_subdev *sd, /* * tvp7002_s_register() - set a control * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to v4l2_control struct + * @reg: ptr to v4l2_dbg_register struct * * Get the value of a TVP7002 decoder device register. * Returns zero when successful, -EINVAL if register read fails or @@ -899,7 +896,7 @@ static int tvp7002_s_register(struct v4l2_subdev *sd, /* * tvp7002_enum_fmt() - Enum supported formats * @sd: pointer to standard V4L2 sub-device structure - * @enable: pointer to format struct + * @fmtdesc: pointer to format struct * * Enumerate supported formats. */ @@ -994,6 +991,23 @@ static int tvp7002_log_status(struct v4l2_subdev *sd) return 0; } +/* + * tvp7002_enum_dv_presets() - Enum supported digital video formats + * @sd: pointer to standard V4L2 sub-device structure + * @preset: pointer to format struct + * + * Enumerate supported digital video formats. + */ +static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, + struct v4l2_dv_enum_preset *preset) +{ + /* Check requested format index is within range */ + if (preset->index >= NUM_PRESETS) + return -EINVAL; + + return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); +} + /* V4L2 core operation handlers */ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { .g_chip_ident = tvp7002_g_chip_ident, @@ -1009,6 +1023,7 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { /* Specific video subsystem operation handlers */ static const struct v4l2_subdev_video_ops tvp7002_video_ops = { + .enum_dv_presets = tvp7002_enum_dv_presets, .s_dv_preset = tvp7002_s_dv_preset, .query_dv_preset = tvp7002_query_dv_preset, .s_stream = tvp7002_s_stream, @@ -1042,8 +1057,8 @@ static struct tvp7002 tvp7002_dev = { /* * tvp7002_probe - Probe a TVP7002 device - * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to i2c_device_id struct + * @c: ptr to i2c_client struct + * @id: ptr to i2c_device_id struct * * Initialize the TVP7002 device * Returns zero when successful, -EINVAL if register read fails or diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index fab48ec6c0e..fbd665fa197 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -693,12 +693,13 @@ static int qcm_start_data(struct uvd *uvd) static void qcm_stop_data(struct uvd *uvd) { - struct qcm *cam = (struct qcm *) uvd->user_data; + 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) diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 083765238a6..42ba2878575 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -244,6 +244,9 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: case CODEC_SAA7111: + /* Without this delay the detection of the saa711x is + hit-and-miss. */ + mdelay(10); v4l2_i2c_new_subdev(&usbvision->v4l2_dev, &usbvision->i2c_adap, "saa7115", "saa7115_auto", 0, saa711x_addrs); diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 7c17ec63c5d..6248a639ba2 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -137,8 +137,6 @@ static int PowerOnAtOpen = 1; static int video_nr = -1; /* Sequential Number of Radio Device */ static int radio_nr = -1; -/* Sequential Number of VBI Device */ -static int vbi_nr = -1; /* Grab parameters for the device driver */ @@ -148,14 +146,12 @@ module_param(video_debug, int, 0444); module_param(PowerOnAtOpen, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); -module_param(vbi_nr, int, 0444); MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened. Default: 1 (On)"); MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); // Misc stuff @@ -1244,36 +1240,6 @@ static int usbvision_radio_close(struct file *file) return errCode; } -/* - * Here comes the stuff for vbi on usbvision based devices - * - */ -static int usbvision_vbi_open(struct file *file) -{ - /* TODO */ - return -ENODEV; -} - -static int usbvision_vbi_close(struct file *file) -{ - /* TODO */ - return -ENODEV; -} - -static long usbvision_do_vbi_ioctl(struct file *file, - unsigned int cmd, void *arg) -{ - /* TODO */ - return -ENOIOCTLCMD; -} - -static long usbvision_vbi_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, usbvision_do_vbi_ioctl); -} - - // // Video registration stuff // @@ -1367,21 +1333,6 @@ static struct video_device usbvision_radio_template = { .current_norm = V4L2_STD_PAL }; -// vbi template -static const struct v4l2_file_operations usbvision_vbi_fops = { - .owner = THIS_MODULE, - .open = usbvision_vbi_open, - .release = usbvision_vbi_close, - .ioctl = usbvision_vbi_ioctl, -}; - -static struct video_device usbvision_vbi_template= -{ - .fops = &usbvision_vbi_fops, - .release = video_device_release, - .name = "usbvision-vbi", -}; - static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, struct video_device *vdev_template, @@ -1410,18 +1361,6 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, // unregister video4linux devices static void usbvision_unregister_video(struct usb_usbvision *usbvision) { - // vbi Device: - if (usbvision->vbi) { - PDEBUG(DBG_PROBE, "unregister %s [v4l2]", - video_device_node_name(usbvision->vbi)); - if (video_is_registered(usbvision->vbi)) { - video_unregister_device(usbvision->vbi); - } else { - video_device_release(usbvision->vbi); - } - usbvision->vbi = NULL; - } - // Radio Device: if (usbvision->rdev) { PDEBUG(DBG_PROBE, "unregister %s [v4l2]", @@ -1482,22 +1421,6 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", usbvision->nr, video_device_node_name(usbvision->rdev)); } - // vbi Device: - if (usbvision_device_data[usbvision->DevModel].vbi) { - usbvision->vbi = usbvision_vdev_init(usbvision, - &usbvision_vbi_template, - "USBVision VBI"); - if (usbvision->vbi == NULL) { - goto err_exit; - } - if (video_register_device(usbvision->vbi, - VFL_TYPE_VBI, - vbi_nr)<0) { - goto err_exit; - } - printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device %s [v4l2] (Not Working Yet!)\n", - usbvision->nr, video_device_node_name(usbvision->vbi)); - } // all done return 0; @@ -1726,8 +1649,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf, usbvision_configure_video(usbvision); mutex_unlock(&usbvision->lock); - - usb_set_intfdata (intf, usbvision); usbvision_create_sysfs(usbvision->vdev); PDEBUG(DBG_PROBE, "success"); @@ -1745,7 +1666,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, */ static void __devexit usbvision_disconnect(struct usb_interface *intf) { - struct usb_usbvision *usbvision = usb_get_intfdata(intf); + struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf)); PDEBUG(DBG_PROBE, ""); @@ -1754,7 +1675,6 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) "%s: usb_get_intfdata() failed\n", __func__); return; } - usb_set_intfdata (intf, NULL); mutex_lock(&usbvision->lock); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index f8d7458daf3..d1b3cc0cd87 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -360,7 +360,6 @@ struct usb_usbvision { struct v4l2_device v4l2_dev; struct video_device *vdev; /* Video Device */ struct video_device *rdev; /* Radio Device */ - struct video_device *vbi; /* VBI Device */ /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; @@ -463,6 +462,11 @@ struct usb_usbvision { int ComprBlockTypes[4]; }; +static inline struct usb_usbvision *to_usbvision(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct usb_usbvision, v4l2_dev); +} + #define call_all(usbvision, o, f, args...) \ v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 6d3850b3716..aa0720af07a 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -217,8 +217,7 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, .index = 4, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_RESTORE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -233,8 +232,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, .index = 6, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -249,8 +249,7 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_IRIS_RELATIVE_CONTROL, .index = 8, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -265,8 +264,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .index = 10, .size = 3, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -281,8 +281,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, .index = 12, .size = 4, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -297,8 +298,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_ROLL_RELATIVE_CONTROL, .index = 14, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -562,6 +564,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, { + .id = V4L2_CID_IRIS_ABSOLUTE, + .name = "Iris, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_IRIS_RELATIVE, + .name = "Iris, Relative", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_RELATIVE_CONTROL, + .size = 8, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { .id = V4L2_CID_ZOOM_ABSOLUTE, .name = "Zoom, Absolute", .entity = UVC_GUID_UVC_CAMERA, @@ -822,6 +844,8 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); v4l2_ctrl->flags = 0; + if (!(ctrl->info->flags & UVC_CONTROL_GET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -1047,6 +1071,8 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (step == 0) + step = 1; xctrl->value = min + (xctrl->value - min + step/2) / step * step; xctrl->value = clamp(xctrl->value, min, max); diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 86ff8c12ea5..838b56f097c 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -91,11 +91,16 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_UYVY, }, { - .name = "Greyscale", + .name = "Greyscale (8-bit)", .guid = UVC_GUID_FORMAT_Y800, .fcc = V4L2_PIX_FMT_GREY, }, { + .name = "Greyscale (16-bit)", + .guid = UVC_GUID_FORMAT_Y16, + .fcc = V4L2_PIX_FMT_Y16, + }, + { .name = "RGB Bayer", .guid = UVC_GUID_FORMAT_BY8, .fcc = V4L2_PIX_FMT_SBGGR8, @@ -2105,6 +2110,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Packard Bell EasyNote MX52 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a12, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Syntek (Asus F9SG) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2169,6 +2183,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Arkmicro unbranded */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3290, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Bodelin ProScopeHR */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_HI diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 4a925a31b0e..133c78d113a 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -388,8 +388,12 @@ unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_wait(file, &buf->wait, wait); if (buf->state == UVC_BUF_STATE_DONE || - buf->state == UVC_BUF_STATE_ERROR) - mask |= POLLIN | POLLRDNORM; + buf->state == UVC_BUF_STATE_ERROR) { + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + mask |= POLLIN | POLLRDNORM; + else + mask |= POLLOUT | POLLWRNORM; + } done: mutex_unlock(&queue->mutex); diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 2bba059259e..d1f88406a5e 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -131,11 +131,13 @@ struct uvc_xu_control { #define UVC_GUID_FORMAT_Y800 \ { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y16 \ + { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} #define UVC_GUID_FORMAT_BY8 \ { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} - /* ------------------------------------------------------------------------ * Driver specific constants. */ diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 36b5cb86fb5..4e53b0b3339 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -51,6 +51,9 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/i2c.h> +#if defined(CONFIG_SPI) +#include <linux/spi/spi.h> +#endif #include <asm/uaccess.h> #include <asm/system.h> #include <asm/pgtable.h> @@ -85,10 +88,9 @@ MODULE_LICENSE("GPL"); val == V4L2_PRIORITY_INTERACTIVE || \ val == V4L2_PRIORITY_RECORD) -int v4l2_prio_init(struct v4l2_prio_state *global) +void v4l2_prio_init(struct v4l2_prio_state *global) { - memset(global,0,sizeof(*global)); - return 0; + memset(global, 0, sizeof(*global)); } EXPORT_SYMBOL(v4l2_prio_init); @@ -108,17 +110,16 @@ int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, } EXPORT_SYMBOL(v4l2_prio_change); -int v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) +void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) { - return v4l2_prio_change(global,local,V4L2_PRIORITY_DEFAULT); + v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT); } EXPORT_SYMBOL(v4l2_prio_open); -int v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority *local) +void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local) { - if (V4L2_PRIO_VALID(*local)) - atomic_dec(&global->prios[*local]); - return 0; + if (V4L2_PRIO_VALID(local)) + atomic_dec(&global->prios[local]); } EXPORT_SYMBOL(v4l2_prio_close); @@ -134,11 +135,9 @@ enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global) } EXPORT_SYMBOL(v4l2_prio_max); -int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local) +int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local) { - if (*local < v4l2_prio_max(global)) - return -EBUSY; - return 0; + return (local < v4l2_prio_max(global)) ? -EBUSY : 0; } EXPORT_SYMBOL(v4l2_prio_check); @@ -340,6 +339,13 @@ const char **v4l2_ctrl_get_menu(u32 id) "None", "Black & White", "Sepia", + "Negative", + "Emboss", + "Sketch", + "Sky blue", + "Grass green", + "Skin whiten", + "Vivid", NULL }; static const char *tune_preemphasis[] = { @@ -429,10 +435,13 @@ const char *v4l2_ctrl_get_name(u32 id) 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"; + case V4L2_CID_BG_COLOR: return "Background Color"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -483,6 +492,8 @@ const char *v4l2_ctrl_get_name(u32 id) 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"; @@ -620,6 +631,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste 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: @@ -636,6 +648,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste 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; @@ -951,6 +964,66 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); #endif /* defined(CONFIG_I2C) */ +#if defined(CONFIG_SPI) + +/* Load a spi sub-device. */ + +void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, + const struct v4l2_subdev_ops *ops) +{ + v4l2_subdev_init(sd, ops); + sd->flags |= V4L2_SUBDEV_FL_IS_SPI; + /* the owner is the same as the spi_device's driver owner */ + sd->owner = spi->dev.driver->owner; + /* spi_device and v4l2_subdev point to one another */ + v4l2_set_subdevdata(sd, spi); + spi_set_drvdata(spi, sd); + /* initialize name */ + strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name)); +} +EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init); + +struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, + struct spi_master *master, struct spi_board_info *info) +{ + struct v4l2_subdev *sd = NULL; + struct spi_device *spi = NULL; + + BUG_ON(!v4l2_dev); + + if (info->modalias) + request_module(info->modalias); + + spi = spi_new_device(master, info); + + if (spi == NULL || spi->dev.driver == NULL) + goto error; + + if (!try_module_get(spi->dev.driver->owner)) + goto error; + + sd = spi_get_drvdata(spi); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(v4l2_dev, sd)) + sd = NULL; + + /* Decrease the module use count to match the first try_module_get. */ + module_put(spi->dev.driver->owner); + +error: + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (spi && sd == NULL) + spi_unregister_device(spi); + + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev); + +#endif /* defined(CONFIG_SPI) */ + /* Clamp x to be between min and max, aligned to a multiple of 2^align. min * and max don't have to be aligned, but there must be at least one valid * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index f77f84bfe71..9004a5fe764 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1086,6 +1086,9 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_QUERY_DV_PRESET: case VIDIOC_S_DV_TIMINGS: case VIDIOC_G_DV_TIMINGS: + case VIDIOC_DQEVENT: + case VIDIOC_SUBSCRIBE_EVENT: + case VIDIOC_UNSUBSCRIBE_EVENT: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 70906991606..0ca7ec9ca90 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -421,6 +421,10 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, if (!vdev->release) return -EINVAL; + /* v4l2_fh support */ + spin_lock_init(&vdev->fh_lock); + INIT_LIST_HEAD(&vdev->fh_list); + /* Part 1: check device type */ switch (type) { case VFL_TYPE_GRABBER: @@ -596,9 +600,7 @@ void video_unregister_device(struct video_device *vdev) if (!vdev || !video_is_registered(vdev)) return; - mutex_lock(&videodev_lock); clear_bit(V4L2_FL_REGISTERED, &vdev->flags); - mutex_unlock(&videodev_lock); device_unregister(&vdev->dev); } EXPORT_SYMBOL(video_unregister_device); diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 0d06e7cbd5b..5a7dc4afe92 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -21,6 +21,9 @@ #include <linux/types.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#if defined(CONFIG_SPI) +#include <linux/spi/spi.h> +#endif #include <linux/videodev2.h> #include <media/v4l2-device.h> @@ -97,6 +100,14 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) i2c_unregister_device(client); } #endif +#if defined(CONFIG_SPI) + if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { + struct spi_device *spi = v4l2_get_subdevdata(sd); + + if (spi) + spi_unregister_device(spi); + } +#endif } } EXPORT_SYMBOL_GPL(v4l2_device_unregister); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c new file mode 100644 index 00000000000..de74ce07b5e --- /dev/null +++ b/drivers/media/video/v4l2-event.c @@ -0,0 +1,292 @@ +/* + * v4l2-event.c + * + * V4L2 events. + * + * Copyright (C) 2009--2010 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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. + * + * 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> + +#include <linux/sched.h> +#include <linux/slab.h> + +int v4l2_event_init(struct v4l2_fh *fh) +{ + fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL); + if (fh->events == NULL) + return -ENOMEM; + + init_waitqueue_head(&fh->events->wait); + + INIT_LIST_HEAD(&fh->events->free); + INIT_LIST_HEAD(&fh->events->available); + INIT_LIST_HEAD(&fh->events->subscribed); + + fh->events->sequence = -1; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_init); + +int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n) +{ + struct v4l2_events *events = fh->events; + unsigned long flags; + + if (!events) { + WARN_ON(1); + return -ENOMEM; + } + + while (events->nallocated < n) { + struct v4l2_kevent *kev; + + kev = kzalloc(sizeof(*kev), GFP_KERNEL); + if (kev == NULL) + return -ENOMEM; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + list_add_tail(&kev->list, &events->free); + events->nallocated++; + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_alloc); + +#define list_kfree(list, type, member) \ + while (!list_empty(list)) { \ + type *hi; \ + hi = list_first_entry(list, type, member); \ + list_del(&hi->member); \ + kfree(hi); \ + } + +void v4l2_event_free(struct v4l2_fh *fh) +{ + struct v4l2_events *events = fh->events; + + if (!events) + return; + + list_kfree(&events->free, struct v4l2_kevent, list); + list_kfree(&events->available, struct v4l2_kevent, list); + list_kfree(&events->subscribed, struct v4l2_subscribed_event, list); + + kfree(events); + fh->events = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_event_free); + +static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) +{ + struct v4l2_events *events = fh->events; + struct v4l2_kevent *kev; + unsigned long flags; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + + if (list_empty(&events->available)) { + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + return -ENOENT; + } + + WARN_ON(events->navailable == 0); + + kev = list_first_entry(&events->available, struct v4l2_kevent, list); + list_move(&kev->list, &events->free); + events->navailable--; + + kev->event.pending = events->navailable; + *event = kev->event; + + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + return 0; +} + +int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, + int nonblocking) +{ + struct v4l2_events *events = fh->events; + int ret; + + if (nonblocking) + return __v4l2_event_dequeue(fh, event); + + do { + ret = wait_event_interruptible(events->wait, + events->navailable != 0); + if (ret < 0) + return ret; + + ret = __v4l2_event_dequeue(fh, event); + } while (ret == -ENOENT); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_event_dequeue); + +/* Caller must hold fh->event->lock! */ +static struct v4l2_subscribed_event *v4l2_event_subscribed( + struct v4l2_fh *fh, u32 type) +{ + struct v4l2_events *events = fh->events; + struct v4l2_subscribed_event *sev; + + assert_spin_locked(&fh->vdev->fh_lock); + + list_for_each_entry(sev, &events->subscribed, list) { + if (sev->type == type) + return sev; + } + + return NULL; +} + +void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) +{ + struct v4l2_fh *fh; + unsigned long flags; + struct timespec timestamp; + + ktime_get_ts(×tamp); + + spin_lock_irqsave(&vdev->fh_lock, flags); + + list_for_each_entry(fh, &vdev->fh_list, list) { + struct v4l2_events *events = fh->events; + struct v4l2_kevent *kev; + + /* Are we subscribed? */ + if (!v4l2_event_subscribed(fh, ev->type)) + continue; + + /* Increase event sequence number on fh. */ + events->sequence++; + + /* Do we have any free events? */ + if (list_empty(&events->free)) + continue; + + /* Take one and fill it. */ + kev = list_first_entry(&events->free, struct v4l2_kevent, list); + kev->event.type = ev->type; + kev->event.u = ev->u; + kev->event.timestamp = timestamp; + kev->event.sequence = events->sequence; + list_move_tail(&kev->list, &events->available); + + events->navailable++; + + wake_up_all(&events->wait); + } + + spin_unlock_irqrestore(&vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_event_queue); + +int v4l2_event_pending(struct v4l2_fh *fh) +{ + return fh->events->navailable; +} +EXPORT_SYMBOL_GPL(v4l2_event_pending); + +int v4l2_event_subscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct v4l2_events *events = fh->events; + struct v4l2_subscribed_event *sev; + unsigned long flags; + + if (fh->events == NULL) { + WARN_ON(1); + return -ENOMEM; + } + + sev = kmalloc(sizeof(*sev), GFP_KERNEL); + if (!sev) + return -ENOMEM; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + + if (v4l2_event_subscribed(fh, sub->type) == NULL) { + INIT_LIST_HEAD(&sev->list); + sev->type = sub->type; + + list_add(&sev->list, &events->subscribed); + sev = NULL; + } + + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + kfree(sev); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_subscribe); + +static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh) +{ + struct v4l2_events *events = fh->events; + struct v4l2_subscribed_event *sev; + unsigned long flags; + + do { + sev = NULL; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + if (!list_empty(&events->subscribed)) { + sev = list_first_entry(&events->subscribed, + struct v4l2_subscribed_event, list); + list_del(&sev->list); + } + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + kfree(sev); + } while (sev); +} + +int v4l2_event_unsubscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct v4l2_subscribed_event *sev; + unsigned long flags; + + if (sub->type == V4L2_EVENT_ALL) { + v4l2_event_unsubscribe_all(fh); + return 0; + } + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + + sev = v4l2_event_subscribed(fh, sub->type); + if (sev != NULL) + list_del(&sev->list); + + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + kfree(sev); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe); diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c new file mode 100644 index 00000000000..d78f184f40c --- /dev/null +++ b/drivers/media/video/v4l2-fh.c @@ -0,0 +1,79 @@ +/* + * v4l2-fh.c + * + * V4L2 file handles. + * + * Copyright (C) 2009--2010 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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. + * + * 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/bitops.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> + +int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) +{ + fh->vdev = vdev; + INIT_LIST_HEAD(&fh->list); + set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); + + /* + * fh->events only needs to be initialized if the driver + * supports the VIDIOC_SUBSCRIBE_EVENT ioctl. + */ + if (vdev->ioctl_ops && vdev->ioctl_ops->vidioc_subscribe_event) + return v4l2_event_init(fh); + + fh->events = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fh_init); + +void v4l2_fh_add(struct v4l2_fh *fh) +{ + unsigned long flags; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + list_add(&fh->list, &fh->vdev->fh_list); + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_fh_add); + +void v4l2_fh_del(struct v4l2_fh *fh) +{ + unsigned long flags; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + list_del_init(&fh->list); + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_fh_del); + +void v4l2_fh_exit(struct v4l2_fh *fh) +{ + if (fh->vdev == NULL) + return; + + fh->vdev = NULL; + + v4l2_event_free(fh); +} +EXPORT_SYMBOL_GPL(v4l2_fh_exit); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 7d59c107f13..0eeceae5032 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -26,6 +26,8 @@ #endif #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/v4l2-chip-ident.h> #define dbgarg(cmd, fmt, arg...) \ @@ -291,6 +293,9 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET", [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS", [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS", + [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", + [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", + [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -610,17 +615,33 @@ static long __video_do_ioctl(struct file *file, void *fh = file->private_data; long ret = -EINVAL; + if (ops == NULL) { + printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", + vfd->name); + return -EINVAL; + } + +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /******************************************************** + All other V4L1 calls are handled by v4l1_compat module. + Those calls will be translated into V4L2 calls, and + __video_do_ioctl will be called again, with one or more + V4L2 ioctls. + ********************************************************/ + if (_IOC_TYPE(cmd) == 'v' && cmd != VIDIOCGMBUF && + _IOC_NR(cmd) < BASE_VIDIOCPRIVATE) { + return v4l_compat_translate_ioctl(file, cmd, arg, + __video_do_ioctl); + } +#endif + if ((vfd->debug & V4L2_DEBUG_IOCTL) && !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { v4l_print_ioctl(vfd->name, cmd); printk(KERN_CONT "\n"); } - if (ops == NULL) { - printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", - vfd->name); - return -EINVAL; - } + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT /*********************************************************** @@ -630,31 +651,21 @@ static long __video_do_ioctl(struct file *file, ***********************************************************/ /* --- streaming capture ------------------------------------- */ - if (cmd == VIDIOCGMBUF) { + case VIDIOCGMBUF: + { struct video_mbuf *p = arg; if (!ops->vidiocgmbuf) - return ret; + break; ret = ops->vidiocgmbuf(file, fh, p); if (!ret) dbgarg(cmd, "size=%d, frames=%d, offsets=0x%08lx\n", p->size, p->frames, (unsigned long)p->offsets); - return ret; + break; } - - /******************************************************** - All other V4L1 calls are handled by v4l1_compat module. - Those calls will be translated into V4L2 calls, and - __video_do_ioctl will be called again, with one or more - V4L2 ioctls. - ********************************************************/ - if (_IOC_TYPE(cmd) == 'v' && _IOC_NR(cmd) < BASE_VIDIOCPRIVATE) - return v4l_compat_translate_ioctl(file, cmd, arg, - __video_do_ioctl); #endif - switch (cmd) { /* --- capabilities ------------------------------------------ */ case VIDIOC_QUERYCAP: { @@ -1072,7 +1083,7 @@ static long __video_do_ioctl(struct file *file, id &= ~curr_id; } if (i <= index) - return -EINVAL; + break; v4l2_video_std_construct(p, curr_id, descr); @@ -1597,7 +1608,7 @@ static long __video_do_ioctl(struct file *file, v4l2_std_id std = vfd->current_norm; if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + break; ret = 0; if (ops->vidioc_g_std) @@ -1942,7 +1953,55 @@ static long __video_do_ioctl(struct file *file, } break; } + case VIDIOC_DQEVENT: + { + struct v4l2_event *ev = arg; + + if (!ops->vidioc_subscribe_event) + break; + ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK); + if (ret < 0) { + dbgarg(cmd, "no pending events?"); + break; + } + dbgarg(cmd, + "pending=%d, type=0x%8.8x, sequence=%d, " + "timestamp=%lu.%9.9lu ", + ev->pending, ev->type, ev->sequence, + ev->timestamp.tv_sec, ev->timestamp.tv_nsec); + break; + } + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + if (!ops->vidioc_subscribe_event) + break; + + ret = ops->vidioc_subscribe_event(fh, sub); + if (ret < 0) { + dbgarg(cmd, "failed, ret=%ld", ret); + break; + } + dbgarg(cmd, "type=0x%8.8x", sub->type); + break; + } + case VIDIOC_UNSUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + if (!ops->vidioc_unsubscribe_event) + break; + + ret = ops->vidioc_unsubscribe_event(fh, sub); + if (ret < 0) { + dbgarg(cmd, "failed, ret=%ld", ret); + break; + } + dbgarg(cmd, "type=0x%8.8x", sub->type); + break; + } default: { if (!ops->vidioc_default) @@ -2006,7 +2065,7 @@ long video_ioctl2(struct file *file, { char sbuf[128]; void *mbuf = NULL; - void *parg = NULL; + void *parg = (void *)arg; long err = -EINVAL; int is_ext_ctrl; size_t ctrls_size = 0; diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c new file mode 100644 index 00000000000..f45f9405ea3 --- /dev/null +++ b/drivers/media/video/v4l2-mem2mem.c @@ -0,0 +1,633 @@ +/* + * Memory-to-memory device framework for Video for Linux 2 and videobuf. + * + * Helper functions for devices that use videobuf buffers for both their + * source and destination. + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <p.osciak@samsung.com> + * Marek Szyprowski, <m.szyprowski@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/sched.h> +#include <linux/slab.h> + +#include <media/videobuf-core.h> +#include <media/v4l2-mem2mem.h> + +MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); +MODULE_AUTHOR("Pawel Osciak, <p.osciak@samsung.com>"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); + +#define dprintk(fmt, arg...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\ + } while (0) + + +/* Instance is already queued on the job_queue */ +#define TRANS_QUEUED (1 << 0) +/* Instance is currently running in hardware */ +#define TRANS_RUNNING (1 << 1) + + +/* Offset base for buffers on the destination queue - used to distinguish + * between source and destination buffers when mmapping - they receive the same + * offsets but for different queues */ +#define DST_QUEUE_OFF_BASE (1 << 30) + + +/** + * struct v4l2_m2m_dev - per-device context + * @curr_ctx: currently running instance + * @job_queue: instances queued to run + * @job_spinlock: protects job_queue + * @m2m_ops: driver callbacks + */ +struct v4l2_m2m_dev { + struct v4l2_m2m_ctx *curr_ctx; + + struct list_head job_queue; + spinlock_t job_spinlock; + + struct v4l2_m2m_ops *m2m_ops; +}; + +static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &m2m_ctx->cap_q_ctx; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &m2m_ctx->out_q_ctx; + default: + printk(KERN_ERR "Invalid buffer type\n"); + return NULL; + } +} + +/** + * v4l2_m2m_get_vq() - return videobuf_queue for the given type + */ +struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + + q_ctx = get_queue_ctx(m2m_ctx, type); + if (!q_ctx) + return NULL; + + return &q_ctx->q; +} +EXPORT_SYMBOL(v4l2_m2m_get_vq); + +/** + * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers + */ +void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + struct videobuf_buffer *vb = NULL; + unsigned long flags; + + q_ctx = get_queue_ctx(m2m_ctx, type); + if (!q_ctx) + return NULL; + + spin_lock_irqsave(q_ctx->q.irqlock, flags); + + if (list_empty(&q_ctx->rdy_queue)) + goto end; + + vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, queue); + vb->state = VIDEOBUF_ACTIVE; + +end: + spin_unlock_irqrestore(q_ctx->q.irqlock, flags); + return vb; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); + +/** + * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and + * return it + */ +void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + struct videobuf_buffer *vb = NULL; + unsigned long flags; + + q_ctx = get_queue_ctx(m2m_ctx, type); + if (!q_ctx) + return NULL; + + spin_lock_irqsave(q_ctx->q.irqlock, flags); + if (!list_empty(&q_ctx->rdy_queue)) { + vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, + queue); + list_del(&vb->queue); + q_ctx->num_rdy--; + } + spin_unlock_irqrestore(q_ctx->q.irqlock, flags); + + return vb; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove); + +/* + * Scheduling handlers + */ + +/** + * v4l2_m2m_get_curr_priv() - return driver private data for the currently + * running instance or NULL if no instance is running + */ +void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev) +{ + unsigned long flags; + void *ret = NULL; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (m2m_dev->curr_ctx) + ret = m2m_dev->curr_ctx->priv; + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + return ret; +} +EXPORT_SYMBOL(v4l2_m2m_get_curr_priv); + +/** + * v4l2_m2m_try_run() - select next job to perform and run it if possible + * + * Get next transaction (if present) from the waiting jobs list and run it. + */ +static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (NULL != m2m_dev->curr_ctx) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Another instance is running, won't run now\n"); + return; + } + + if (list_empty(&m2m_dev->job_queue)) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("No job pending\n"); + return; + } + + m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next, + struct v4l2_m2m_ctx, queue); + m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING; + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv); +} + +/** + * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to + * the pending job queue and add it if so. + * @m2m_ctx: m2m context assigned to the instance to be checked + * + * There are three basic requirements an instance has to meet to be able to run: + * 1) at least one source buffer has to be queued, + * 2) at least one destination buffer has to be queued, + * 3) streaming has to be on. + * + * There may also be additional, custom requirements. In such case the driver + * should supply a custom callback (job_ready in v4l2_m2m_ops) that should + * return 1 if the instance is ready. + * An example of the above could be an instance that requires more than one + * src/dst buffer per transaction. + */ +static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev; + unsigned long flags_job, flags; + + m2m_dev = m2m_ctx->m2m_dev; + dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); + + if (!m2m_ctx->out_q_ctx.q.streaming + || !m2m_ctx->cap_q_ctx.q.streaming) { + dprintk("Streaming needs to be on for both queues\n"); + return; + } + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job); + if (m2m_ctx->job_flags & TRANS_QUEUED) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("On job queue already\n"); + return; + } + + spin_lock_irqsave(m2m_ctx->out_q_ctx.q.irqlock, flags); + if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { + spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("No input buffers available\n"); + return; + } + if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { + spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("No output buffers available\n"); + return; + } + spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + + if (m2m_dev->m2m_ops->job_ready + && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("Driver not ready\n"); + return; + } + + list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue); + m2m_ctx->job_flags |= TRANS_QUEUED; + + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + + v4l2_m2m_try_run(m2m_dev); +} + +/** + * v4l2_m2m_job_finish() - inform the framework that a job has been finished + * and have it clean up + * + * Called by a driver to yield back the device after it has finished with it. + * Should be called as soon as possible after reaching a state which allows + * other instances to take control of the device. + * + * This function has to be called only after device_run() callback has been + * called on the driver. To prevent recursion, it should not be called directly + * from the device_run() callback though. + */ +void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Called by an instance not currently running\n"); + return; + } + + list_del(&m2m_dev->curr_ctx->queue); + m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + m2m_dev->curr_ctx = NULL; + + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + /* This instance might have more buffers ready, but since we do not + * allow more than one job on the job_queue per instance, each has + * to be scheduled separately after the previous one finishes. */ + v4l2_m2m_try_schedule(m2m_ctx); + v4l2_m2m_try_run(m2m_dev); +} +EXPORT_SYMBOL(v4l2_m2m_job_finish); + +/** + * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer + */ +int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_requestbuffers *reqbufs) +{ + struct videobuf_queue *vq; + + vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type); + return videobuf_reqbufs(vq, reqbufs); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); + +/** + * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer + * + * See v4l2_m2m_mmap() documentation for details. + */ +int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct videobuf_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + ret = videobuf_querybuf(vq, buf); + + if (buf->memory == V4L2_MEMORY_MMAP + && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + buf->m.offset += DST_QUEUE_OFF_BASE; + } + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); + +/** + * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on + * the type + */ +int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct videobuf_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + ret = videobuf_qbuf(vq, buf); + if (!ret) + v4l2_m2m_try_schedule(m2m_ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); + +/** + * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on + * the type + */ +int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct videobuf_queue *vq; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + return videobuf_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); + +/** + * v4l2_m2m_streamon() - turn on streaming for a video queue + */ +int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + struct videobuf_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, type); + ret = videobuf_streamon(vq); + if (!ret) + v4l2_m2m_try_schedule(m2m_ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_streamon); + +/** + * v4l2_m2m_streamoff() - turn off streaming for a video queue + */ +int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + struct videobuf_queue *vq; + + vq = v4l2_m2m_get_vq(m2m_ctx, type); + return videobuf_streamoff(vq); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); + +/** + * v4l2_m2m_poll() - poll replacement, for destination buffers only + * + * Call from the driver's poll() function. Will poll both queues. If a buffer + * is available to dequeue (with dqbuf) from the source queue, this will + * indicate that a non-blocking write can be performed, while read will be + * returned in case of the destination queue. + */ +unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct poll_table_struct *wait) +{ + struct videobuf_queue *src_q, *dst_q; + struct videobuf_buffer *src_vb = NULL, *dst_vb = NULL; + unsigned int rc = 0; + + src_q = v4l2_m2m_get_src_vq(m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); + + mutex_lock(&src_q->vb_lock); + mutex_lock(&dst_q->vb_lock); + + if (src_q->streaming && !list_empty(&src_q->stream)) + src_vb = list_first_entry(&src_q->stream, + struct videobuf_buffer, stream); + if (dst_q->streaming && !list_empty(&dst_q->stream)) + dst_vb = list_first_entry(&dst_q->stream, + struct videobuf_buffer, stream); + + if (!src_vb && !dst_vb) { + rc = POLLERR; + goto end; + } + + if (src_vb) { + poll_wait(file, &src_vb->done, wait); + if (src_vb->state == VIDEOBUF_DONE + || src_vb->state == VIDEOBUF_ERROR) + rc |= POLLOUT | POLLWRNORM; + } + if (dst_vb) { + poll_wait(file, &dst_vb->done, wait); + if (dst_vb->state == VIDEOBUF_DONE + || dst_vb->state == VIDEOBUF_ERROR) + rc |= POLLIN | POLLRDNORM; + } + +end: + mutex_unlock(&dst_q->vb_lock); + mutex_unlock(&src_q->vb_lock); + return rc; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_poll); + +/** + * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer + * + * Call from driver's mmap() function. Will handle mmap() for both queues + * seamlessly for videobuffer, which will receive normal per-queue offsets and + * proper videobuf queue pointers. The differentiation is made outside videobuf + * by adding a predefined offset to buffers from one of the queues and + * subtracting it before passing it back to videobuf. Only drivers (and + * thus applications) receive modified offsets. + */ +int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct vm_area_struct *vma) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct videobuf_queue *vq; + + if (offset < DST_QUEUE_OFF_BASE) { + vq = v4l2_m2m_get_src_vq(m2m_ctx); + } else { + vq = v4l2_m2m_get_dst_vq(m2m_ctx); + vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); + } + + return videobuf_mmap_mapper(vq, vma); +} +EXPORT_SYMBOL(v4l2_m2m_mmap); + +/** + * v4l2_m2m_init() - initialize per-driver m2m data + * + * Usually called from driver's probe() function. + */ +struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) +{ + struct v4l2_m2m_dev *m2m_dev; + + if (!m2m_ops) + return ERR_PTR(-EINVAL); + + BUG_ON(!m2m_ops->device_run); + BUG_ON(!m2m_ops->job_abort); + + m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL); + if (!m2m_dev) + return ERR_PTR(-ENOMEM); + + m2m_dev->curr_ctx = NULL; + m2m_dev->m2m_ops = m2m_ops; + INIT_LIST_HEAD(&m2m_dev->job_queue); + spin_lock_init(&m2m_dev->job_spinlock); + + return m2m_dev; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_init); + +/** + * v4l2_m2m_release() - cleans up and frees a m2m_dev structure + * + * Usually called from driver's remove() function. + */ +void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev) +{ + kfree(m2m_dev); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_release); + +/** + * v4l2_m2m_ctx_init() - allocate and initialize a m2m context + * @priv - driver's instance private data + * @m2m_dev - a previously initialized m2m_dev struct + * @vq_init - a callback for queue type-specific initialization function to be + * used for initializing videobuf_queues + * + * Usually called from driver's open() function. + */ +struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev, + void (*vq_init)(void *priv, struct videobuf_queue *, + enum v4l2_buf_type)) +{ + struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx; + + if (!vq_init) + return ERR_PTR(-EINVAL); + + m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL); + if (!m2m_ctx) + return ERR_PTR(-ENOMEM); + + m2m_ctx->priv = priv; + m2m_ctx->m2m_dev = m2m_dev; + + out_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + cap_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + INIT_LIST_HEAD(&out_q_ctx->rdy_queue); + INIT_LIST_HEAD(&cap_q_ctx->rdy_queue); + + INIT_LIST_HEAD(&m2m_ctx->queue); + + vq_init(priv, &out_q_ctx->q, V4L2_BUF_TYPE_VIDEO_OUTPUT); + vq_init(priv, &cap_q_ctx->q, V4L2_BUF_TYPE_VIDEO_CAPTURE); + out_q_ctx->q.priv_data = cap_q_ctx->q.priv_data = priv; + + return m2m_ctx; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); + +/** + * v4l2_m2m_ctx_release() - release m2m context + * + * Usually called from driver's release() function. + */ +void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev; + struct videobuf_buffer *vb; + unsigned long flags; + + m2m_dev = m2m_ctx->m2m_dev; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (m2m_ctx->job_flags & TRANS_RUNNING) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); + dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); + vb = v4l2_m2m_next_dst_buf(m2m_ctx); + BUG_ON(NULL == vb); + wait_event(vb->done, vb->state != VIDEOBUF_ACTIVE + && vb->state != VIDEOBUF_QUEUED); + } else if (m2m_ctx->job_flags & TRANS_QUEUED) { + list_del(&m2m_ctx->queue); + m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("m2m_ctx: %p had been on queue and was removed\n", + m2m_ctx); + } else { + /* Do nothing, was not on queue/running */ + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + } + + videobuf_stop(&m2m_ctx->cap_q_ctx.q); + videobuf_stop(&m2m_ctx->out_q_ctx.q); + + videobuf_mmap_free(&m2m_ctx->cap_q_ctx.q); + videobuf_mmap_free(&m2m_ctx->out_q_ctx.q); + + kfree(m2m_ctx); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release); + +/** + * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list. + * + * Call from buf_queue(), videobuf_queue_ops callback. + * + * Locking: Caller holds q->irqlock (taken by videobuf before calling buf_queue + * callback in the driver). + */ +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + + q_ctx = get_queue_ctx(m2m_ctx, vq->type); + if (!q_ctx) + return; + + list_add_tail(&vb->queue, &q_ctx->rdy_queue); + q_ctx->num_rdy++; + + vb->state = VIDEOBUF_QUEUED; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); + diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index bb0a1c8de41..7d3378437de 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -24,10 +24,15 @@ #include <media/videobuf-core.h> #define MAGIC_BUFFER 0x20070728 -#define MAGIC_CHECK(is, should) do { \ - if (unlikely((is) != (should))) { \ - printk(KERN_ERR "magic mismatch: %x (expected %x)\n", is, should); \ - BUG(); } } while (0) +#define MAGIC_CHECK(is, should) \ + do { \ + if (unlikely((is) != (should))) { \ + printk(KERN_ERR \ + "magic mismatch: %x (expected %x)\n", \ + is, should); \ + BUG(); \ + } \ + } while (0) static int debug; module_param(debug, int, 0644); @@ -36,16 +41,18 @@ MODULE_DESCRIPTION("helper module to manage video4linux buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); -#define dprintk(level, fmt, arg...) do { \ - if (debug >= level) \ - printk(KERN_DEBUG "vbuf: " fmt , ## arg); } while (0) +#define dprintk(level, fmt, arg...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG "vbuf: " fmt, ## arg); \ + } while (0) /* --------------------------------------------------------------------- */ #define CALL(q, f, arg...) \ ((q->int_ops->f) ? q->int_ops->f(arg) : 0) -void *videobuf_alloc(struct videobuf_queue *q) +struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) { struct videobuf_buffer *vb; @@ -57,14 +64,14 @@ void *videobuf_alloc(struct videobuf_queue *q) } vb = q->int_ops->alloc(q->msize); - if (NULL != vb) { init_waitqueue_head(&vb->done); - vb->magic = MAGIC_BUFFER; + vb->magic = MAGIC_BUFFER; } return vb; } +EXPORT_SYMBOL_GPL(videobuf_alloc); #define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\ vb->state != VIDEOBUF_QUEUED) @@ -86,6 +93,7 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) return 0; } +EXPORT_SYMBOL_GPL(videobuf_waiton); int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) @@ -95,16 +103,16 @@ int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, return CALL(q, iolock, q, vb, fbuf); } +EXPORT_SYMBOL_GPL(videobuf_iolock); -void *videobuf_queue_to_vmalloc (struct videobuf_queue *q, - struct videobuf_buffer *buf) +void *videobuf_queue_to_vaddr(struct videobuf_queue *q, + struct videobuf_buffer *buf) { - if (q->int_ops->vmalloc) - return q->int_ops->vmalloc(buf); - else - return NULL; + if (q->int_ops->vaddr) + return q->int_ops->vaddr(buf); + return NULL; } -EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); +EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr); /* --------------------------------------------------------------------- */ @@ -146,6 +154,7 @@ void videobuf_queue_core_init(struct videobuf_queue *q, init_waitqueue_head(&q->wait); INIT_LIST_HEAD(&q->stream); } +EXPORT_SYMBOL_GPL(videobuf_queue_core_init); /* Locking: Only usage in bttv unsafe find way to remove */ int videobuf_queue_is_busy(struct videobuf_queue *q) @@ -184,6 +193,7 @@ int videobuf_queue_is_busy(struct videobuf_queue *q) } return 0; } +EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); /* Locking: Caller holds q->vb_lock */ void videobuf_queue_cancel(struct videobuf_queue *q) @@ -216,6 +226,7 @@ void videobuf_queue_cancel(struct videobuf_queue *q) } INIT_LIST_HEAD(&q->stream); } +EXPORT_SYMBOL_GPL(videobuf_queue_cancel); /* --------------------------------------------------------------------- */ @@ -237,6 +248,7 @@ enum v4l2_field videobuf_next_field(struct videobuf_queue *q) } return field; } +EXPORT_SYMBOL_GPL(videobuf_next_field); /* Locking: Caller holds q->vb_lock */ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, @@ -273,8 +285,10 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, case VIDEOBUF_ACTIVE: b->flags |= V4L2_BUF_FLAG_QUEUED; break; - case VIDEOBUF_DONE: case VIDEOBUF_ERROR: + b->flags |= V4L2_BUF_FLAG_ERROR; + /* fall through */ + case VIDEOBUF_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; case VIDEOBUF_NEEDS_INIT: @@ -298,20 +312,15 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, static int __videobuf_mmap_free(struct videobuf_queue *q) { int i; - int rc; if (!q) return 0; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - rc = CALL(q, mmap_free, q); - - q->is_mmapped = 0; - - if (rc < 0) - return rc; + 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]) @@ -321,7 +330,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q) q->bufs[i] = NULL; } - return rc; + return 0; } int videobuf_mmap_free(struct videobuf_queue *q) @@ -332,6 +341,7 @@ int videobuf_mmap_free(struct videobuf_queue *q) mutex_unlock(&q->vb_lock); return ret; } +EXPORT_SYMBOL_GPL(videobuf_mmap_free); /* Locking: Caller holds q->vb_lock */ int __videobuf_mmap_setup(struct videobuf_queue *q, @@ -351,7 +361,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, for (i = 0; i < bcount; i++) { q->bufs[i] = videobuf_alloc(q); - if (q->bufs[i] == NULL) + if (NULL == q->bufs[i]) break; q->bufs[i]->i = i; @@ -372,11 +382,11 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, if (!i) return -ENOMEM; - dprintk(1, "mmap setup: %d buffers, %d bytes each\n", - i, bsize); + dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize); return i; } +EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); int videobuf_mmap_setup(struct videobuf_queue *q, unsigned int bcount, unsigned int bsize, @@ -388,6 +398,7 @@ int videobuf_mmap_setup(struct videobuf_queue *q, mutex_unlock(&q->vb_lock); return ret; } +EXPORT_SYMBOL_GPL(videobuf_mmap_setup); int videobuf_reqbufs(struct videobuf_queue *q, struct v4l2_requestbuffers *req) @@ -432,7 +443,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, q->ops->buf_setup(q, &count, &size); dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", count, size, - (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) ); + (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT)); retval = __videobuf_mmap_setup(q, count, size, req->memory); if (retval < 0) { @@ -447,6 +458,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_reqbufs); int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) { @@ -473,9 +485,9 @@ done: mutex_unlock(&q->vb_lock); return ret; } +EXPORT_SYMBOL_GPL(videobuf_querybuf); -int videobuf_qbuf(struct videobuf_queue *q, - struct v4l2_buffer *b) +int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) { struct videobuf_buffer *buf; enum v4l2_field field; @@ -534,6 +546,13 @@ int videobuf_qbuf(struct videobuf_queue *q, "but buffer addr is zero!\n"); goto done; } + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT + || q->type == V4L2_BUF_TYPE_VBI_OUTPUT + || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + buf->size = b->bytesused; + buf->field = b->field; + buf->ts = b->timestamp; + } break; case V4L2_MEMORY_USERPTR: if (b->length < buf->bsize) { @@ -567,11 +586,11 @@ int videobuf_qbuf(struct videobuf_queue *q, q->ops->buf_queue(q, buf); spin_unlock_irqrestore(q->irqlock, flags); } - dprintk(1, "qbuf: succeded\n"); + dprintk(1, "qbuf: succeeded\n"); retval = 0; wake_up_interruptible_sync(&q->wait); - done: +done: mutex_unlock(&q->vb_lock); if (b->memory == V4L2_MEMORY_MMAP) @@ -579,7 +598,7 @@ int videobuf_qbuf(struct videobuf_queue *q, return retval; } - +EXPORT_SYMBOL_GPL(videobuf_qbuf); /* Locking: Caller holds q->vb_lock */ static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock) @@ -624,7 +643,6 @@ done: return retval; } - /* Locking: Caller holds q->vb_lock */ static int stream_next_buffer(struct videobuf_queue *q, struct videobuf_buffer **vb, int nonblocking) @@ -647,13 +665,14 @@ done: } int videobuf_dqbuf(struct videobuf_queue *q, - struct v4l2_buffer *b, int nonblocking) + struct v4l2_buffer *b, int nonblocking) { struct videobuf_buffer *buf = NULL; int retval; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + memset(b, 0, sizeof(*b)); mutex_lock(&q->vb_lock); retval = stream_next_buffer(q, &buf, nonblocking); @@ -665,28 +684,25 @@ int videobuf_dqbuf(struct videobuf_queue *q, switch (buf->state) { case VIDEOBUF_ERROR: dprintk(1, "dqbuf: state is error\n"); - retval = -EIO; - CALL(q, sync, q, buf); - buf->state = VIDEOBUF_IDLE; break; case VIDEOBUF_DONE: dprintk(1, "dqbuf: state is done\n"); - CALL(q, sync, q, buf); - buf->state = VIDEOBUF_IDLE; break; default: dprintk(1, "dqbuf: state invalid\n"); retval = -EINVAL; goto done; } - list_del(&buf->stream); - memset(b, 0, sizeof(*b)); + CALL(q, sync, q, buf); videobuf_status(q, b, buf, q->type); - - done: + list_del(&buf->stream); + buf->state = VIDEOBUF_IDLE; + b->flags &= ~V4L2_BUF_FLAG_DONE; +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_dqbuf); int videobuf_streamon(struct videobuf_queue *q) { @@ -709,10 +725,11 @@ int videobuf_streamon(struct videobuf_queue *q) spin_unlock_irqrestore(q->irqlock, flags); wake_up_interruptible_sync(&q->wait); - done: +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_streamon); /* Locking: Caller holds q->vb_lock */ static int __videobuf_streamoff(struct videobuf_queue *q) @@ -735,6 +752,7 @@ int videobuf_streamoff(struct videobuf_queue *q) return retval; } +EXPORT_SYMBOL_GPL(videobuf_streamoff); /* Locking: Caller holds q->vb_lock */ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, @@ -774,7 +792,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, retval = q->read_buf->size; } - done: +done: /* cleanup */ q->ops->buf_release(q, q->read_buf); kfree(q->read_buf); @@ -782,6 +800,49 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, return retval; } +static int __videobuf_copy_to_user(struct videobuf_queue *q, + struct videobuf_buffer *buf, + char __user *data, size_t count, + int nonblocking) +{ + void *vaddr = CALL(q, vaddr, buf); + + /* copy to userspace */ + if (count > buf->size - q->read_off) + count = buf->size - q->read_off; + + if (copy_to_user(data, vaddr + q->read_off, count)) + return -EFAULT; + + return count; +} + +static int __videobuf_copy_stream(struct videobuf_queue *q, + struct videobuf_buffer *buf, + char __user *data, size_t count, size_t pos, + int vbihack, int nonblocking) +{ + unsigned int *fc = CALL(q, vaddr, buf); + + if (vbihack) { + /* dirty, undocumented hack -- pass the frame counter + * within the last four bytes of each vbi data block. + * We need that one to maintain backward compatibility + * to all vbi decoding software out there ... */ + fc += (buf->size >> 2) - 1; + *fc = buf->field_count >> 1; + dprintk(1, "vbihack: %d\n", *fc); + } + + /* copy stuff using the common method */ + count = __videobuf_copy_to_user(q, buf, data, count, nonblocking); + + if ((count == -EFAULT) && (pos == 0)) + return -EFAULT; + + return count; +} + ssize_t videobuf_read_one(struct videobuf_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblocking) @@ -850,7 +911,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, } /* Copy to userspace */ - retval = CALL(q, video_copy_to_user, q, data, count, nonblocking); + retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking); if (retval < 0) goto done; @@ -862,10 +923,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, q->read_buf = NULL; } - done: +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_read_one); /* Locking: Caller holds q->vb_lock */ static int __videobuf_read_start(struct videobuf_queue *q) @@ -917,7 +979,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q) q->bufs[i] = NULL; } q->read_buf = NULL; - } int videobuf_read_start(struct videobuf_queue *q) @@ -930,6 +991,7 @@ int videobuf_read_start(struct videobuf_queue *q) return rc; } +EXPORT_SYMBOL_GPL(videobuf_read_start); void videobuf_read_stop(struct videobuf_queue *q) { @@ -937,6 +999,7 @@ void videobuf_read_stop(struct videobuf_queue *q) __videobuf_read_stop(q); mutex_unlock(&q->vb_lock); } +EXPORT_SYMBOL_GPL(videobuf_read_stop); void videobuf_stop(struct videobuf_queue *q) { @@ -950,7 +1013,7 @@ void videobuf_stop(struct videobuf_queue *q) mutex_unlock(&q->vb_lock); } - +EXPORT_SYMBOL_GPL(videobuf_stop); ssize_t videobuf_read_stream(struct videobuf_queue *q, char __user *data, size_t count, loff_t *ppos, @@ -990,7 +1053,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, } if (q->read_buf->state == VIDEOBUF_DONE) { - rc = CALL(q, copy_stream, q, data + retval, count, + rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count, retval, vbihack, nonblocking); if (rc < 0) { retval = rc; @@ -1019,10 +1082,11 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, break; } - done: +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_read_stream); unsigned int videobuf_poll_stream(struct file *file, struct videobuf_queue *q, @@ -1056,27 +1120,51 @@ unsigned int videobuf_poll_stream(struct file *file, if (0 == rc) { poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || - buf->state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; + buf->state == VIDEOBUF_ERROR) { + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + rc = POLLOUT | POLLWRNORM; + break; + default: + rc = POLLIN | POLLRDNORM; + break; + } + } } mutex_unlock(&q->vb_lock); return rc; } +EXPORT_SYMBOL_GPL(videobuf_poll_stream); -int videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) +int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) { - int retval; + int rc = -EINVAL; + int i; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { + dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n"); + return -EINVAL; + } + mutex_lock(&q->vb_lock); - retval = CALL(q, mmap_mapper, q, vma); - q->is_mmapped = 1; + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + struct videobuf_buffer *buf = q->bufs[i]; + + if (buf && buf->memory == V4L2_MEMORY_MMAP && + buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) { + rc = CALL(q, mmap_mapper, q, buf, vma); + break; + } + } mutex_unlock(&q->vb_lock); - return retval; + return rc; } +EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); #ifdef CONFIG_VIDEO_V4L1_COMPAT int videobuf_cgmbuf(struct videobuf_queue *q, @@ -1107,33 +1195,3 @@ int videobuf_cgmbuf(struct videobuf_queue *q, EXPORT_SYMBOL_GPL(videobuf_cgmbuf); #endif -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL_GPL(videobuf_waiton); -EXPORT_SYMBOL_GPL(videobuf_iolock); - -EXPORT_SYMBOL_GPL(videobuf_alloc); - -EXPORT_SYMBOL_GPL(videobuf_queue_core_init); -EXPORT_SYMBOL_GPL(videobuf_queue_cancel); -EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); - -EXPORT_SYMBOL_GPL(videobuf_next_field); -EXPORT_SYMBOL_GPL(videobuf_reqbufs); -EXPORT_SYMBOL_GPL(videobuf_querybuf); -EXPORT_SYMBOL_GPL(videobuf_qbuf); -EXPORT_SYMBOL_GPL(videobuf_dqbuf); -EXPORT_SYMBOL_GPL(videobuf_streamon); -EXPORT_SYMBOL_GPL(videobuf_streamoff); - -EXPORT_SYMBOL_GPL(videobuf_read_start); -EXPORT_SYMBOL_GPL(videobuf_read_stop); -EXPORT_SYMBOL_GPL(videobuf_stop); -EXPORT_SYMBOL_GPL(videobuf_read_stream); -EXPORT_SYMBOL_GPL(videobuf_read_one); -EXPORT_SYMBOL_GPL(videobuf_poll_stream); - -EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); -EXPORT_SYMBOL_GPL(videobuf_mmap_setup); -EXPORT_SYMBOL_GPL(videobuf_mmap_free); -EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index dce4f3aa4af..74730c624cf 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -55,14 +55,14 @@ static void videobuf_vm_close(struct vm_area_struct *vma) struct videobuf_queue *q = map->q; int i; - dev_dbg(map->q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", + dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); map->count--; if (0 == map->count) { struct videobuf_dma_contig_memory *mem; - dev_dbg(map->q->dev, "munmap %p q=%p\n", map, q); + dev_dbg(q->dev, "munmap %p q=%p\n", map, q); mutex_lock(&q->vb_lock); /* We need first to cancel streams, before unmapping */ @@ -89,7 +89,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) /* vfree is not atomic - can't be called with IRQ's disabled */ - dev_dbg(map->q->dev, "buf[%d] freeing %p\n", + dev_dbg(q->dev, "buf[%d] freeing %p\n", i, mem->vaddr); dma_free_coherent(q->dev, mem->size, @@ -190,7 +190,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, return ret; } -static void *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -204,7 +204,7 @@ static void *__videobuf_alloc(size_t size) return vb; } -static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf) +static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -263,65 +263,34 @@ static int __videobuf_iolock(struct videobuf_queue *q, return 0; } -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - unsigned int i; - - dev_dbg(q->dev, "%s\n", __func__); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (q->bufs[i] && q->bufs[i]->map) - return -EBUSY; - } - - return 0; -} - static int __videobuf_mmap_mapper(struct videobuf_queue *q, + struct videobuf_buffer *buf, struct vm_area_struct *vma) { struct videobuf_dma_contig_memory *mem; struct videobuf_mapping *map; - unsigned int first; int retval; - unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long size; dev_dbg(q->dev, "%s\n", __func__); - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - /* look for first buffer to map */ - for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (!q->bufs[first]) - continue; - - if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) - continue; - if (q->bufs[first]->boff == offset) - break; - } - if (VIDEO_MAX_FRAME == first) { - dev_dbg(q->dev, "invalid user space offset [offset=0x%lx]\n", - offset); - return -EINVAL; - } /* create mapping + update buffer list */ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (!map) return -ENOMEM; - q->bufs[first]->map = map; + buf->map = map; map->start = vma->vm_start; map->end = vma->vm_end; map->q = q; - q->bufs[first]->baddr = vma->vm_start; + buf->baddr = vma->vm_start; - mem = q->bufs[first]->priv; + mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - mem->size = PAGE_ALIGN(q->bufs[first]->bsize); + mem->size = PAGE_ALIGN(buf->bsize); mem->vaddr = dma_alloc_coherent(q->dev, mem->size, &mem->dma_handle, GFP_KERNEL); if (!mem->vaddr) { @@ -354,8 +323,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", map, q, vma->vm_start, vma->vm_end, - (long int) q->bufs[first]->bsize, - vma->vm_pgoff, first); + (long int)buf->bsize, + vma->vm_pgoff, buf->i); videobuf_vm_open(vma); @@ -366,69 +335,13 @@ error: return -ENOMEM; } -static int __videobuf_copy_to_user(struct videobuf_queue *q, - char __user *data, size_t count, - int nonblocking) -{ - struct videobuf_dma_contig_memory *mem = q->read_buf->priv; - void *vaddr; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - BUG_ON(!mem->vaddr); - - /* copy to userspace */ - if (count > q->read_buf->size - q->read_off) - count = q->read_buf->size - q->read_off; - - vaddr = mem->vaddr; - - if (copy_to_user(data, vaddr + q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream(struct videobuf_queue *q, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking) -{ - unsigned int *fc; - struct videobuf_dma_contig_memory *mem = q->read_buf->priv; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc = (unsigned int *)mem->vaddr; - fc += (q->read_buf->size >> 2) - 1; - *fc = q->read_buf->field_count >> 1; - dev_dbg(q->dev, "vbihack: %d\n", *fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user(q, data, count, nonblocking); - - if ((count == -EFAULT) && (pos == 0)) - return -EFAULT; - - return count; -} - static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, - .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, - .video_copy_to_user = __videobuf_copy_to_user, - .copy_stream = __videobuf_copy_stream, - .vmalloc = __videobuf_to_vmalloc, + .vaddr = __videobuf_to_vaddr, }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index fcd045e7a1c..8359e6badd3 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -37,8 +37,12 @@ #define MAGIC_DMABUF 0x19721112 #define MAGIC_SG_MEM 0x17890714 -#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ - { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } +#define MAGIC_CHECK(is, should) \ + if (unlikely((is) != (should))) { \ + printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \ + is, should); \ + BUG(); \ + } static int debug; module_param(debug, int, 0644); @@ -47,13 +51,13 @@ MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg) +#define dprintk(level, fmt, arg...) \ + if (debug >= level) \ + printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg) /* --------------------------------------------------------------------- */ -struct scatterlist* -videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) +struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) { struct scatterlist *sglist; struct page *pg; @@ -73,13 +77,14 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) } return sglist; - err: +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) +struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, + int offset) { struct scatterlist *sglist; int i; @@ -104,20 +109,20 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) } return sglist; - nopage: - dprintk(2,"sgl: oops - no page\n"); +nopage: + dprintk(2, "sgl: oops - no page\n"); vfree(sglist); return NULL; - highmem: - dprintk(2,"sgl: oops - highmem page\n"); +highmem: + dprintk(2, "sgl: oops - highmem page\n"); vfree(sglist); return NULL; } /* --------------------------------------------------------------------- */ -struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf) +struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf) { struct videobuf_dma_sg_memory *mem = buf->priv; BUG_ON(!mem); @@ -126,17 +131,19 @@ struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf) return &mem->dma; } +EXPORT_SYMBOL_GPL(videobuf_to_dma); void videobuf_dma_init(struct videobuf_dmabuf *dma) { - memset(dma,0,sizeof(*dma)); + memset(dma, 0, sizeof(*dma)); dma->magic = MAGIC_DMABUF; } +EXPORT_SYMBOL_GPL(videobuf_dma_init); static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { - unsigned long first,last; + unsigned long first, last; int err, rw = 0; dma->direction = direction; @@ -155,21 +162,21 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT; dma->offset = data & ~PAGE_MASK; dma->nr_pages = last-first+1; - dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*), - GFP_KERNEL); + dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL); if (NULL == dma->pages) return -ENOMEM; - dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", - data,size,dma->nr_pages); - err = get_user_pages(current,current->mm, + dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", + data, size, dma->nr_pages); + + err = get_user_pages(current, current->mm, data & PAGE_MASK, dma->nr_pages, rw == READ, 1, /* force */ dma->pages, NULL); if (err != dma->nr_pages) { dma->nr_pages = (err >= 0) ? err : 0; - dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages); + dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages); return err < 0 ? err : -EINVAL; } return 0; @@ -179,48 +186,58 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { int ret; + down_read(¤t->mm->mmap_sem); ret = videobuf_dma_init_user_locked(dma, direction, data, size); up_read(¤t->mm->mmap_sem); return ret; } +EXPORT_SYMBOL_GPL(videobuf_dma_init_user); int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { - dprintk(1,"init kernel [%d pages]\n",nr_pages); + dprintk(1, "init kernel [%d pages]\n", nr_pages); + dma->direction = direction; dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == dma->vmalloc) { - dprintk(1,"vmalloc_32(%d pages) failed\n",nr_pages); + dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); return -ENOMEM; } - dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n", + + dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", (unsigned long)dma->vmalloc, nr_pages << PAGE_SHIFT); - memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT); + + memset(dma->vmalloc, 0, nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, dma_addr_t addr, int nr_pages) { - dprintk(1,"init overlay [%d pages @ bus 0x%lx]\n", - nr_pages,(unsigned long)addr); + dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n", + nr_pages, (unsigned long)addr); dma->direction = direction; + if (0 == addr) return -EINVAL; dma->bus_addr = addr; dma->nr_pages = nr_pages; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) +int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) { - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); if (dma->pages) { @@ -228,20 +245,21 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) dma->offset); } if (dma->vmalloc) { - dma->sglist = videobuf_vmalloc_to_sg - (dma->vmalloc,dma->nr_pages); + dma->sglist = videobuf_vmalloc_to_sg(dma->vmalloc, + dma->nr_pages); } if (dma->bus_addr) { dma->sglist = vmalloc(sizeof(*dma->sglist)); if (NULL != dma->sglist) { - dma->sglen = 1; - sg_dma_address(&dma->sglist[0]) = dma->bus_addr & PAGE_MASK; - dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK; - sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE; + dma->sglen = 1; + sg_dma_address(&dma->sglist[0]) = dma->bus_addr + & PAGE_MASK; + dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK; + sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE; } } if (NULL == dma->sglist) { - dprintk(1,"scatterlist is NULL\n"); + dprintk(1, "scatterlist is NULL\n"); return -ENOMEM; } if (!dma->bus_addr) { @@ -249,47 +267,43 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) dma->nr_pages, dma->direction); if (0 == dma->sglen) { printk(KERN_WARNING - "%s: videobuf_map_sg failed\n",__func__); + "%s: videobuf_map_sg failed\n", __func__); vfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; return -ENOMEM; } } - return 0; -} - -int videobuf_dma_sync(struct videobuf_queue *q, struct videobuf_dmabuf *dma) -{ - MAGIC_CHECK(dma->magic, MAGIC_DMABUF); - BUG_ON(!dma->sglen); - dma_sync_sg_for_cpu(q->dev, dma->sglist, dma->nr_pages, dma->direction); return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_map); -int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma) +int videobuf_dma_unmap(struct videobuf_queue *q, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); + if (!dma->sglen) return 0; - dma_unmap_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction); + dma_unmap_sg(q->dev, dma->sglist, dma->sglen, dma->direction); vfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_unmap); int videobuf_dma_free(struct videobuf_dmabuf *dma) { - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + int i; + MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(dma->sglen); if (dma->pages) { - int i; - for (i=0; i < dma->nr_pages; i++) + for (i = 0; i < dma->nr_pages; i++) page_cache_release(dma->pages[i]); kfree(dma->pages); dma->pages = NULL; @@ -298,12 +312,13 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) vfree(dma->vmalloc); dma->vmalloc = NULL; - if (dma->bus_addr) { + if (dma->bus_addr) dma->bus_addr = 0; - } dma->direction = DMA_NONE; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_free); /* --------------------------------------------------------------------- */ @@ -315,6 +330,7 @@ int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma) 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) { @@ -324,49 +340,48 @@ int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) return videobuf_dma_unmap(&q, dma); } +EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); /* --------------------------------------------------------------------- */ -static void -videobuf_vm_open(struct vm_area_struct *vma) +static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); + map->count++; } -static void -videobuf_vm_close(struct vm_area_struct *vma) +static void videobuf_vm_close(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; struct videobuf_queue *q = map->q; struct videobuf_dma_sg_memory *mem; int i; - dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); map->count--; if (0 == map->count) { - dprintk(1,"munmap %p q=%p\n",map,q); + dprintk(1, "munmap %p q=%p\n", map, q); mutex_lock(&q->vb_lock); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; - mem=q->bufs[i]->priv; - + mem = q->bufs[i]->priv; if (!mem) continue; - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); if (q->bufs[i]->map != map) continue; q->bufs[i]->map = NULL; q->bufs[i]->baddr = 0; - q->ops->buf_release(q,q->bufs[i]); + q->ops->buf_release(q, q->bufs[i]); } mutex_unlock(&q->vb_lock); kfree(map); @@ -380,26 +395,27 @@ videobuf_vm_close(struct vm_area_struct *vma) * now ...). Bounce buffers don't work very well for the data rates * video capture has. */ -static int -videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page; - dprintk(3,"fault: fault @ %08lx [vma %08lx-%08lx]\n", - (unsigned long)vmf->virtual_address,vma->vm_start,vma->vm_end); + dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n", + (unsigned long)vmf->virtual_address, + vma->vm_start, vma->vm_end); + page = alloc_page(GFP_USER | __GFP_DMA32); if (!page) return VM_FAULT_OOM; clear_user_highpage(page, (unsigned long)vmf->virtual_address); vmf->page = page; + return 0; } -static const struct vm_operations_struct videobuf_vm_ops = -{ - .open = videobuf_vm_open, - .close = videobuf_vm_close, - .fault = videobuf_vm_fault, +static const struct vm_operations_struct videobuf_vm_ops = { + .open = videobuf_vm_open, + .close = videobuf_vm_close, + .fault = videobuf_vm_fault, }; /* --------------------------------------------------------------------- @@ -412,28 +428,28 @@ static const struct vm_operations_struct videobuf_vm_ops = struct videobuf_dma_sg_memory */ -static void *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_dma_sg_memory *mem; struct videobuf_buffer *vb; - vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); + vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); if (!vb) return vb; - mem = vb->priv = ((char *)vb)+size; - mem->magic=MAGIC_SG_MEM; + mem = vb->priv = ((char *)vb) + size; + mem->magic = MAGIC_SG_MEM; videobuf_dma_init(&mem->dma); - dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), - mem,(long)sizeof(*mem)); + dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n", + __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), + mem, (long)sizeof(*mem)); return vb; } -static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf) +static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_sg_memory *mem = buf->priv; BUG_ON(!mem); @@ -443,11 +459,11 @@ static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf) return mem->dma.vmalloc; } -static int __videobuf_iolock (struct videobuf_queue* q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) +static int __videobuf_iolock(struct videobuf_queue *q, + struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf) { - int err,pages; + int err, pages; dma_addr_t bus; struct videobuf_dma_sg_memory *mem = vb->priv; BUG_ON(!mem); @@ -460,16 +476,16 @@ static int __videobuf_iolock (struct videobuf_queue* q, if (0 == vb->baddr) { /* no userspace addr -- kernel bounce buffer */ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; - err = videobuf_dma_init_kernel( &mem->dma, - DMA_FROM_DEVICE, - pages ); + err = videobuf_dma_init_kernel(&mem->dma, + DMA_FROM_DEVICE, + pages); if (0 != err) return err; } else if (vb->memory == V4L2_MEMORY_USERPTR) { /* dma directly to userspace */ - err = videobuf_dma_init_user( &mem->dma, - DMA_FROM_DEVICE, - vb->baddr,vb->bsize ); + err = videobuf_dma_init_user(&mem->dma, + DMA_FROM_DEVICE, + vb->baddr, vb->bsize); if (0 != err) return err; } else { @@ -515,43 +531,27 @@ static int __videobuf_sync(struct videobuf_queue *q, struct videobuf_buffer *buf) { struct videobuf_dma_sg_memory *mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); + BUG_ON(!mem || !mem->dma.sglen); - return videobuf_dma_sync(q,&mem->dma); -} - -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - int i; + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (q->bufs[i]) { - if (q->bufs[i]->map) - return -EBUSY; - } - } + dma_sync_sg_for_cpu(q->dev, mem->dma.sglist, + mem->dma.sglen, mem->dma.direction); return 0; } static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) + struct videobuf_buffer *buf, + struct vm_area_struct *vma) { - struct videobuf_dma_sg_memory *mem; + struct videobuf_dma_sg_memory *mem = buf->priv; struct videobuf_mapping *map; - unsigned int first,last,size,i; + unsigned int first, last, size = 0, i; int retval; retval = -EINVAL; - if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1,"mmap app bug: PROT_WRITE please\n"); - goto done; - } - if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1,"mmap app bug: MAP_SHARED please\n"); - goto done; - } /* This function maintains backwards compatibility with V4L1 and will * map more than one buffer if the vma length is equal to the combined @@ -561,48 +561,52 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, * TODO: Allow drivers to specify if they support this mode */ + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + /* look for first buffer to map */ for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (NULL == q->bufs[first]) - continue; - mem=q->bufs[first]->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); - - if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) - continue; - if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + if (buf == q->bufs[first]) { + size = PAGE_ALIGN(q->bufs[first]->bsize); break; + } } - if (VIDEO_MAX_FRAME == first) { - dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n", - (vma->vm_pgoff << PAGE_SHIFT)); + + /* paranoia, should never happen since buf is always valid. */ + if (!size) { + dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); goto done; } - /* look for last buffer to map */ - for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { - if (NULL == q->bufs[last]) - continue; - if (V4L2_MEMORY_MMAP != q->bufs[last]->memory) - continue; - if (q->bufs[last]->map) { - retval = -EBUSY; + last = first; +#ifdef CONFIG_VIDEO_V4L1_COMPAT + if (size != (vma->vm_end - vma->vm_start)) { + /* look for last buffer to map */ + for (last = first + 1; last < VIDEO_MAX_FRAME; last++) { + if (NULL == q->bufs[last]) + continue; + if (V4L2_MEMORY_MMAP != q->bufs[last]->memory) + continue; + if (q->bufs[last]->map) { + retval = -EBUSY; + goto done; + } + size += PAGE_ALIGN(q->bufs[last]->bsize); + if (size == (vma->vm_end - vma->vm_start)) + break; + } + if (VIDEO_MAX_FRAME == last) { + dprintk(1, "mmap app bug: size invalid [size=0x%lx]\n", + (vma->vm_end - vma->vm_start)); goto done; } - size += PAGE_ALIGN(q->bufs[last]->bsize); - if (size == (vma->vm_end - vma->vm_start)) - break; - } - if (VIDEO_MAX_FRAME == last) { - dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n", - (vma->vm_end - vma->vm_start)); - goto done; } +#endif /* create mapping + update buffer list */ retval = -ENOMEM; - map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); + map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (NULL == map) goto done; @@ -623,72 +627,22 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ vma->vm_private_data = map; - dprintk(1,"mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n", - map,q,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last); + dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n", + map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last); retval = 0; - done: +done: return retval; } -static int __videobuf_copy_to_user ( struct videobuf_queue *q, - char __user *data, size_t count, - int nonblocking ) -{ - struct videobuf_dma_sg_memory *mem = q->read_buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); - - /* copy to userspace */ - if (count > q->read_buf->size - q->read_off) - count = q->read_buf->size - q->read_off; - - if (copy_to_user(data, mem->dma.vmalloc+q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream ( struct videobuf_queue *q, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking ) -{ - unsigned int *fc; - struct videobuf_dma_sg_memory *mem = q->read_buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc = (unsigned int*)mem->dma.vmalloc; - fc += (q->read_buf->size>>2) -1; - *fc = q->read_buf->field_count >> 1; - dprintk(1,"vbihack: %d\n",*fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user (q,data,count,nonblocking); - - if ( (count==-EFAULT) && (0 == pos) ) - return -EFAULT; - - return count; -} - static struct videobuf_qtype_ops sg_ops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, .sync = __videobuf_sync, - .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, - .video_copy_to_user = __videobuf_copy_to_user, - .copy_stream = __videobuf_copy_stream, - .vmalloc = __videobuf_to_vmalloc, + .vaddr = __videobuf_to_vaddr, }; void *videobuf_sg_alloc(size_t size) @@ -702,8 +656,9 @@ void *videobuf_sg_alloc(size_t size) return videobuf_alloc(&q); } +EXPORT_SYMBOL_GPL(videobuf_sg_alloc); -void videobuf_queue_sg_init(struct videobuf_queue* q, +void videobuf_queue_sg_init(struct videobuf_queue *q, const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, @@ -715,29 +670,5 @@ void videobuf_queue_sg_init(struct videobuf_queue* q, videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, priv, &sg_ops); } - -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); - -EXPORT_SYMBOL_GPL(videobuf_to_dma); -EXPORT_SYMBOL_GPL(videobuf_dma_init); -EXPORT_SYMBOL_GPL(videobuf_dma_init_user); -EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); -EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -EXPORT_SYMBOL_GPL(videobuf_dma_map); -EXPORT_SYMBOL_GPL(videobuf_dma_sync); -EXPORT_SYMBOL_GPL(videobuf_dma_unmap); -EXPORT_SYMBOL_GPL(videobuf_dma_free); - -EXPORT_SYMBOL_GPL(videobuf_sg_dma_map); -EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); -EXPORT_SYMBOL_GPL(videobuf_sg_alloc); - EXPORT_SYMBOL_GPL(videobuf_queue_sg_init); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 0afb62e63d9..3f76398968b 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -67,7 +67,7 @@ static int videobuf_dvb_thread(void *data) try_to_freeze(); /* feed buffer data to demux */ - outp = videobuf_queue_to_vmalloc (&dvb->dvbq, buf); + outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf); if (buf->state == VIDEOBUF_DONE) dvb_dmx_swfilter(&dvb->demux, outp, diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 136e09383c0..583728f4c22 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -30,8 +30,12 @@ #define MAGIC_DMABUF 0x17760309 #define MAGIC_VMAL_MEM 0x18221223 -#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ - { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } +#define MAGIC_CHECK(is, should) \ + if (unlikely((is) != (should))) { \ + printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \ + is, should); \ + BUG(); \ + } static int debug; module_param(debug, int, 0644); @@ -40,19 +44,19 @@ MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg) +#define dprintk(level, fmt, arg...) \ + if (debug >= level) \ + printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg) /***************************************************************************/ -static void -videobuf_vm_open(struct vm_area_struct *vma) +static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - dprintk(2,"vm_open %p [count=%u,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); map->count++; } @@ -63,7 +67,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) struct videobuf_queue *q = map->q; int i; - dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, + dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); map->count--; @@ -116,8 +120,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) return; } -static const struct vm_operations_struct videobuf_vm_ops = -{ +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, }; @@ -132,28 +135,28 @@ static const struct vm_operations_struct videobuf_vm_ops = struct videobuf_dma_sg_memory */ -static void *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_vmalloc_memory *mem; struct videobuf_buffer *vb; - vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); + vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); if (!vb) return vb; - mem = vb->priv = ((char *)vb)+size; - mem->magic=MAGIC_VMAL_MEM; + mem = vb->priv = ((char *)vb) + size; + mem->magic = MAGIC_VMAL_MEM; - dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), - mem,(long)sizeof(*mem)); + dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n", + __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), + mem, (long)sizeof(*mem)); return vb; } -static int __videobuf_iolock (struct videobuf_queue* q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) +static int __videobuf_iolock(struct videobuf_queue *q, + struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf) { struct videobuf_vmalloc_memory *mem = vb->priv; int pages; @@ -177,15 +180,13 @@ static int __videobuf_iolock (struct videobuf_queue* q, dprintk(1, "%s memory method USERPTR\n", __func__); -#if 1 if (vb->baddr) { printk(KERN_ERR "USERPTR is currently not supported\n"); return -EINVAL; } -#endif /* The only USERPTR currently supported is the one needed for - read() method. + * read() method. */ mem->vmalloc = vmalloc_user(pages); @@ -210,7 +211,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, /* Try to remap memory */ rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); if (rc < 0) { - printk(KERN_ERR "mmap: remap failed with error %d. ", rc); + printk(KERN_ERR "mmap: remap failed with error %d", rc); return -ENOMEM; } #endif @@ -228,69 +229,29 @@ static int __videobuf_iolock (struct videobuf_queue* q, return 0; } -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - return 0; -} - -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - unsigned int i; - - dprintk(1, "%s\n", __func__); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (q->bufs[i]) { - if (q->bufs[i]->map) - return -EBUSY; - } - } - - return 0; -} - static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) + struct videobuf_buffer *buf, + struct vm_area_struct *vma) { struct videobuf_vmalloc_memory *mem; struct videobuf_mapping *map; - unsigned int first; int retval, pages; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; dprintk(1, "%s\n", __func__); - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - /* look for first buffer to map */ - for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (NULL == q->bufs[first]) - continue; - - if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) - continue; - if (q->bufs[first]->boff == offset) - break; - } - if (VIDEO_MAX_FRAME == first) { - dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n", - (vma->vm_pgoff << PAGE_SHIFT)); - return -EINVAL; - } /* create mapping + update buffer list */ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (NULL == map) return -ENOMEM; - q->bufs[first]->map = map; + buf->map = map; map->start = vma->vm_start; map->end = vma->vm_end; map->q = q; - q->bufs[first]->baddr = vma->vm_start; + buf->baddr = vma->vm_start; - mem = q->bufs[first]->priv; + mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); @@ -300,8 +261,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, 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->vmalloc, pages); /* Try to remap memory */ retval = remap_vmalloc_range(vma, mem->vmalloc, 0); @@ -315,10 +275,10 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; vma->vm_private_data = map; - dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", + dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", map, q, vma->vm_start, vma->vm_end, - (long int) q->bufs[first]->bsize, - vma->vm_pgoff, first); + (long int)buf->bsize, + vma->vm_pgoff, buf->i); videobuf_vm_open(vma); @@ -330,69 +290,16 @@ error: return -ENOMEM; } -static int __videobuf_copy_to_user ( struct videobuf_queue *q, - char __user *data, size_t count, - int nonblocking ) -{ - struct videobuf_vmalloc_memory *mem=q->read_buf->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); - - BUG_ON (!mem->vmalloc); - - /* copy to userspace */ - if (count > q->read_buf->size - q->read_off) - count = q->read_buf->size - q->read_off; - - if (copy_to_user(data, mem->vmalloc+q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream ( struct videobuf_queue *q, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking ) -{ - unsigned int *fc; - struct videobuf_vmalloc_memory *mem=q->read_buf->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc = (unsigned int*)mem->vmalloc; - fc += (q->read_buf->size>>2) -1; - *fc = q->read_buf->field_count >> 1; - dprintk(1,"vbihack: %d\n",*fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user (q,data,count,nonblocking); - - if ( (count==-EFAULT) && (0 == pos) ) - return -EFAULT; - - return count; -} - static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, - .sync = __videobuf_sync, - .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, - .video_copy_to_user = __videobuf_copy_to_user, - .copy_stream = __videobuf_copy_stream, - .vmalloc = videobuf_to_vmalloc, + .vaddr = videobuf_to_vmalloc, }; -void videobuf_queue_vmalloc_init(struct videobuf_queue* q, +void videobuf_queue_vmalloc_init(struct videobuf_queue *q, const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, @@ -404,20 +311,19 @@ void videobuf_queue_vmalloc_init(struct videobuf_queue* q, videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, priv, &qops); } - EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init); -void *videobuf_to_vmalloc (struct videobuf_buffer *buf) +void *videobuf_to_vmalloc(struct videobuf_buffer *buf) { - struct videobuf_vmalloc_memory *mem=buf->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); + struct videobuf_vmalloc_memory *mem = buf->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); return mem->vmalloc; } EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); -void videobuf_vmalloc_free (struct videobuf_buffer *buf) +void videobuf_vmalloc_free(struct videobuf_buffer *buf) { struct videobuf_vmalloc_memory *mem = buf->priv; @@ -442,8 +348,3 @@ void videobuf_vmalloc_free (struct videobuf_buffer *buf) } EXPORT_SYMBOL_GPL(videobuf_vmalloc_free); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index cdbe70385c1..e17b6fee046 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -13,29 +13,23 @@ * License, or (at your option) any later version */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/errno.h> -#include <linux/fs.h> #include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/ioport.h> #include <linux/init.h> #include <linux/sched.h> -#include <linux/pci.h> -#include <linux/random.h> +#include <linux/slab.h> +#include <linux/font.h> #include <linux/version.h> #include <linux/mutex.h> #include <linux/videodev2.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> #include <linux/kthread.h> -#include <linux/highmem.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) #include <linux/freezer.h> +#endif #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include "font.h" +#include <media/v4l2-common.h> #define VIVI_MODULE_NAME "vivi" @@ -44,8 +38,11 @@ #define WAKE_DENOMINATOR 1001 #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ +#define MAX_WIDTH 1920 +#define MAX_HEIGHT 1200 + #define VIVI_MAJOR_VERSION 0 -#define VIVI_MINOR_VERSION 6 +#define VIVI_MINOR_VERSION 7 #define VIVI_RELEASE 0 #define VIVI_VERSION \ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE) @@ -70,56 +67,8 @@ static unsigned int vid_limit = 16; module_param(vid_limit, uint, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); - -/* supported controls */ -static struct v4l2_queryctrl vivi_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 65535, - .flags = V4L2_CTRL_FLAG_SLIDER, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 127, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 0x10, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 127, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_SLIDER, - } -}; +/* Global font descriptor */ +static const u8 *font8x16; #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) @@ -214,41 +163,38 @@ struct vivi_dev { struct list_head vivi_devlist; struct v4l2_device v4l2_dev; + /* controls */ + int brightness; + int contrast; + int saturation; + int hue; + int volume; + spinlock_t slock; struct mutex mutex; - int users; - /* various device info */ struct video_device *vfd; struct vivi_dmaqueue vidq; /* Several counters */ - int h, m, s, ms; + unsigned ms; unsigned long jiffies; - char timestr[13]; int mv_count; /* Controls bars movement */ /* Input Number */ int input; - /* Control 'registers' */ - int qctl_regs[ARRAY_SIZE(vivi_qctrl)]; -}; - -struct vivi_fh { - struct vivi_dev *dev; - /* video capture */ struct vivi_fmt *fmt; unsigned int width, height; struct videobuf_queue vb_vidq; - enum v4l2_buf_type type; - unsigned char bars[8][3]; - int input; /* Input Number on bars */ + unsigned long generating; + u8 bars[9][3]; + u8 line[MAX_WIDTH * 4]; }; /* ------------------------------------------------------------------ @@ -259,19 +205,20 @@ struct vivi_fh { enum colors { WHITE, - AMBAR, + AMBER, CYAN, GREEN, MAGENTA, RED, BLUE, BLACK, + TEXT_BLACK, }; - /* R G B */ +/* R G B */ #define COLOR_WHITE {204, 204, 204} -#define COLOR_AMBAR {208, 208, 0} -#define COLOR_CIAN { 0, 206, 206} +#define COLOR_AMBER {208, 208, 0} +#define COLOR_CYAN { 0, 206, 206} #define COLOR_GREEN { 0, 239, 0} #define COLOR_MAGENTA {239, 0, 239} #define COLOR_RED {205, 0, 0} @@ -279,56 +226,24 @@ enum colors { #define COLOR_BLACK { 0, 0, 0} struct bar_std { - u8 bar[8][3]; + u8 bar[9][3]; }; /* Maximum number of bars are 10 - otherwise, the input print code should be modified */ static struct bar_std bars[] = { { /* Standard ITU-R color bar sequence */ - { - COLOR_WHITE, - COLOR_AMBAR, - COLOR_CIAN, - COLOR_GREEN, - COLOR_MAGENTA, - COLOR_RED, - COLOR_BLUE, - COLOR_BLACK, - } + { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN, + COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK } }, { - { - COLOR_WHITE, - COLOR_AMBAR, - COLOR_BLACK, - COLOR_WHITE, - COLOR_AMBAR, - COLOR_BLACK, - COLOR_WHITE, - COLOR_AMBAR, - } + { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, + COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK } }, { - { - COLOR_WHITE, - COLOR_CIAN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_CIAN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_CIAN, - } + { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, + COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK } }, { - { - COLOR_WHITE, - COLOR_GREEN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_GREEN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_GREEN, - } + { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, + COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK } }, }; @@ -344,21 +259,18 @@ static struct bar_std bars[] = { (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128) /* precalculate color bar values to speed up rendering */ -static void precalculate_bars(struct vivi_fh *fh) +static void precalculate_bars(struct vivi_dev *dev) { - struct vivi_dev *dev = fh->dev; - unsigned char r, g, b; + u8 r, g, b; int k, is_yuv; - fh->input = dev->input; - - for (k = 0; k < 8; k++) { - r = bars[fh->input].bar[k][0]; - g = bars[fh->input].bar[k][1]; - b = bars[fh->input].bar[k][2]; + for (k = 0; k < 9; k++) { + r = bars[dev->input].bar[k][0]; + g = bars[dev->input].bar[k][1]; + b = bars[dev->input].bar[k][2]; is_yuv = 0; - switch (fh->fmt->fourcc) { + switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: is_yuv = 1; @@ -378,16 +290,15 @@ static void precalculate_bars(struct vivi_fh *fh) } if (is_yuv) { - fh->bars[k][0] = TO_Y(r, g, b); /* Luma */ - fh->bars[k][1] = TO_U(r, g, b); /* Cb */ - fh->bars[k][2] = TO_V(r, g, b); /* Cr */ + dev->bars[k][0] = TO_Y(r, g, b); /* Luma */ + dev->bars[k][1] = TO_U(r, g, b); /* Cb */ + dev->bars[k][2] = TO_V(r, g, b); /* Cr */ } else { - fh->bars[k][0] = r; - fh->bars[k][1] = g; - fh->bars[k][2] = b; + dev->bars[k][0] = r; + dev->bars[k][1] = g; + dev->bars[k][2] = b; } } - } #define TSTAMP_MIN_Y 24 @@ -395,20 +306,20 @@ static void precalculate_bars(struct vivi_fh *fh) #define TSTAMP_INPUT_X 10 #define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) -static void gen_twopix(struct vivi_fh *fh, unsigned char *buf, int colorpos) +static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) { - unsigned char r_y, g_u, b_v; - unsigned char *p; + u8 r_y, g_u, b_v; int color; + u8 *p; - r_y = fh->bars[colorpos][0]; /* R or precalculated Y */ - g_u = fh->bars[colorpos][1]; /* G or precalculated U */ - b_v = fh->bars[colorpos][2]; /* B or precalculated V */ + r_y = dev->bars[colorpos][0]; /* R or precalculated Y */ + g_u = dev->bars[colorpos][1]; /* G or precalculated U */ + b_v = dev->bars[colorpos][2]; /* B or precalculated V */ for (color = 0; color < 4; color++) { p = buf + color; - switch (fh->fmt->fourcc) { + switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: switch (color) { case 0: @@ -489,123 +400,88 @@ static void gen_twopix(struct vivi_fh *fh, unsigned char *buf, int colorpos) } } -static void gen_line(struct vivi_fh *fh, char *basep, int inipos, int wmax, - int hmax, int line, int count, char *timestr) +static void precalculate_line(struct vivi_dev *dev) { - int w, i, j; - int pos = inipos; - char *s; - u8 chr; - - /* We will just duplicate the second pixel at the packet */ - wmax /= 2; + int w; - /* Generate a standard color bar pattern */ - for (w = 0; w < wmax; w++) { - int colorpos = ((w + count) * 8/(wmax + 1)) % 8; + for (w = 0; w < dev->width * 2; w += 2) { + int colorpos = (w / (dev->width / 8) % 8); - gen_twopix(fh, basep + pos, colorpos); - pos += 4; /* only 16 bpp supported for now */ + gen_twopix(dev, dev->line + w * 2, colorpos); } +} - /* Prints input entry number */ - - /* Checks if it is possible to input number */ - if (TSTAMP_MAX_Y >= hmax) - goto end; - - if (TSTAMP_INPUT_X + strlen(timestr) >= wmax) - goto end; - - if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) { - chr = rom8x16_bits[fh->input * 16 + line - TSTAMP_MIN_Y]; - pos = TSTAMP_INPUT_X; - for (i = 0; i < 7; i++) { - /* Draw white font on black background */ - if (chr & 1 << (7 - i)) - gen_twopix(fh, basep + pos, WHITE); - else - gen_twopix(fh, basep + pos, BLACK); - pos += 2; - } - } +static void gen_text(struct vivi_dev *dev, char *basep, + int y, int x, char *text) +{ + int line; - /* Checks if it is possible to show timestamp */ - if (TSTAMP_MIN_X + strlen(timestr) >= wmax) - goto end; + /* Checks if it is possible to show string */ + if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width) + return; /* Print stream time */ - if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) { - j = TSTAMP_MIN_X; - for (s = timestr; *s; s++) { - chr = rom8x16_bits[(*s-0x30)*16+line-TSTAMP_MIN_Y]; - for (i = 0; i < 7; i++) { - pos = inipos + j * 2; + for (line = y; line < y + 16; line++) { + int j = 0; + char *pos = basep + line * dev->width * 2 + x * 2; + char *s; + + for (s = text; *s; s++) { + u8 chr = font8x16[*s * 16 + line - y]; + int i; + + for (i = 0; i < 7; i++, j++) { /* Draw white font on black background */ - if (chr & 1 << (7 - i)) - gen_twopix(fh, basep + pos, WHITE); + if (chr & (1 << (7 - i))) + gen_twopix(dev, pos + j * 2, WHITE); else - gen_twopix(fh, basep + pos, BLACK); - j++; + gen_twopix(dev, pos + j * 2, TEXT_BLACK); } } } - -end: - return; } -static void vivi_fillbuff(struct vivi_fh *fh, struct vivi_buffer *buf) +static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - struct vivi_dev *dev = fh->dev; - int h , pos = 0; - int hmax = buf->vb.height; - int wmax = buf->vb.width; + int hmax = buf->vb.height; + int wmax = buf->vb.width; struct timeval ts; - char *tmpbuf; void *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned ms; + char str[100]; + int h, line = 1; if (!vbuf) return; - tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); - if (!tmpbuf) - return; - - for (h = 0; h < hmax; h++) { - gen_line(fh, tmpbuf, 0, wmax, hmax, h, dev->mv_count, - dev->timestr); - memcpy(vbuf + pos, tmpbuf, wmax * 2); - pos += wmax*2; - } - - dev->mv_count++; - - kfree(tmpbuf); + for (h = 0; h < hmax; h++) + memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2); /* Updates stream time */ - dev->ms += jiffies_to_msecs(jiffies-dev->jiffies); + dev->ms += jiffies_to_msecs(jiffies - dev->jiffies); dev->jiffies = jiffies; - if (dev->ms >= 1000) { - dev->ms -= 1000; - dev->s++; - if (dev->s >= 60) { - dev->s -= 60; - dev->m++; - if (dev->m > 60) { - dev->m -= 60; - dev->h++; - if (dev->h > 24) - dev->h -= 24; - } - } - } - sprintf(dev->timestr, "%02d:%02d:%02d:%03d", - dev->h, dev->m, dev->s, dev->ms); - - dprintk(dev, 2, "vivifill at %s: Buffer 0x%08lx size= %d\n", - dev->timestr, (unsigned long)tmpbuf, pos); + ms = dev->ms; + snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ", + (ms / (60 * 60 * 1000)) % 24, + (ms / (60 * 1000)) % 60, + (ms / 1000) % 60, + ms % 1000); + gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " %dx%d, input %d ", + dev->width, dev->height, dev->input); + gen_text(dev, vbuf, line++ * 16, 16, str); + + snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", + dev->brightness, + dev->contrast, + dev->saturation, + dev->hue); + gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " volume %3d ", dev->volume); + gen_text(dev, vbuf, line++ * 16, 16, str); + + dev->mv_count += 2; /* Advice that buffer was filled */ buf->vb.field_count++; @@ -614,12 +490,10 @@ static void vivi_fillbuff(struct vivi_fh *fh, struct vivi_buffer *buf) buf->vb.state = VIDEOBUF_DONE; } -static void vivi_thread_tick(struct vivi_fh *fh) +static void vivi_thread_tick(struct vivi_dev *dev) { - struct vivi_buffer *buf; - struct vivi_dev *dev = fh->dev; struct vivi_dmaqueue *dma_q = &dev->vidq; - + struct vivi_buffer *buf; unsigned long flags = 0; dprintk(dev, 1, "Thread tick\n"); @@ -642,22 +516,20 @@ static void vivi_thread_tick(struct vivi_fh *fh) do_gettimeofday(&buf->vb.ts); /* Fill buffer */ - vivi_fillbuff(fh, buf); + vivi_fillbuff(dev, buf); dprintk(dev, 1, "filled buffer %p\n", buf); wake_up(&buf->vb.done); dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i); unlock: spin_unlock_irqrestore(&dev->slock, flags); - return; } #define frames_to_ms(frames) \ ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) -static void vivi_sleep(struct vivi_fh *fh) +static void vivi_sleep(struct vivi_dev *dev) { - struct vivi_dev *dev = fh->dev; struct vivi_dmaqueue *dma_q = &dev->vidq; int timeout; DECLARE_WAITQUEUE(wait, current); @@ -672,7 +544,7 @@ static void vivi_sleep(struct vivi_fh *fh) /* Calculate time to wake up */ timeout = msecs_to_jiffies(frames_to_ms(1)); - vivi_thread_tick(fh); + vivi_thread_tick(dev); schedule_timeout_interruptible(timeout); @@ -683,15 +555,14 @@ stop_task: static int vivi_thread(void *data) { - struct vivi_fh *fh = data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = data; dprintk(dev, 1, "thread started\n"); set_freezable(); for (;;) { - vivi_sleep(fh); + vivi_sleep(dev); if (kthread_should_stop()) break; @@ -700,39 +571,61 @@ static int vivi_thread(void *data) return 0; } -static int vivi_start_thread(struct vivi_fh *fh) +static void vivi_start_generating(struct file *file) { - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); struct vivi_dmaqueue *dma_q = &dev->vidq; - dma_q->frame = 0; - dma_q->ini_jiffies = jiffies; - dprintk(dev, 1, "%s\n", __func__); - dma_q->kthread = kthread_run(vivi_thread, fh, "vivi"); + if (test_and_set_bit(0, &dev->generating)) + return; + file->private_data = dev; + + /* Resets frame counters */ + dev->ms = 0; + dev->mv_count = 0; + dev->jiffies = jiffies; + + dma_q->frame = 0; + dma_q->ini_jiffies = jiffies; + dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name); if (IS_ERR(dma_q->kthread)) { v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); - return PTR_ERR(dma_q->kthread); + clear_bit(0, &dev->generating); + return; } /* Wakes thread */ wake_up_interruptible(&dma_q->wq); dprintk(dev, 1, "returning from %s\n", __func__); - return 0; } -static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) +static void vivi_stop_generating(struct file *file) { - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); + struct vivi_dev *dev = video_drvdata(file); + struct vivi_dmaqueue *dma_q = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); + + if (!file->private_data) + return; + if (!test_and_clear_bit(0, &dev->generating)) + return; + /* shutdown control thread */ if (dma_q->kthread) { kthread_stop(dma_q->kthread); dma_q->kthread = NULL; } + videobuf_stop(&dev->vb_vidq); + videobuf_mmap_free(&dev->vb_vidq); +} + +static int vivi_is_generating(struct vivi_dev *dev) +{ + return test_bit(0, &dev->generating); } /* ------------------------------------------------------------------ @@ -741,10 +634,9 @@ static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; - *size = fh->width*fh->height*2; + *size = dev->width * dev->height * 2; if (0 == *count) *count = 32; @@ -760,49 +652,43 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) { - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state); - if (in_interrupt()) - BUG(); - videobuf_vmalloc_free(&buf->vb); dprintk(dev, 1, "free_buffer: freed\n"); buf->vb.state = VIDEOBUF_NEEDS_INIT; } -#define norm_maxw() 1024 -#define norm_maxh() 768 static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); int rc; dprintk(dev, 1, "%s, field=%d\n", __func__, field); - BUG_ON(NULL == fh->fmt); + BUG_ON(NULL == dev->fmt); - if (fh->width < 48 || fh->width > norm_maxw() || - fh->height < 32 || fh->height > norm_maxh()) + if (dev->width < 48 || dev->width > MAX_WIDTH || + dev->height < 32 || dev->height > MAX_HEIGHT) return -EINVAL; - buf->vb.size = fh->width*fh->height*2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + buf->vb.size = dev->width * dev->height * 2; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; /* These properties only change when queue is idle, see s_fmt */ - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; + buf->fmt = dev->fmt; + buf->vb.width = dev->width; + buf->vb.height = dev->height; buf->vb.field = field; - precalculate_bars(fh); + precalculate_bars(dev); + precalculate_line(dev); if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); @@ -811,7 +697,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, } buf->vb.state = VIDEOBUF_PREPARED; - return 0; fail: @@ -822,9 +707,8 @@ fail: static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; + struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); struct vivi_dmaqueue *vidq = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); @@ -836,9 +720,8 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = (struct vivi_dev *)fh->dev; + struct vivi_dev *dev = vq->priv_data; + struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -858,16 +741,14 @@ static struct videobuf_queue_ops vivi_video_qops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); strcpy(cap->driver, "vivi"); strcpy(cap->card, "vivi"); strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); cap->version = VIVI_VERSION; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \ + V4L2_CAP_READWRITE; return 0; } @@ -889,28 +770,25 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - 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->fourcc; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = dev->vb_vidq.field; + f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; + (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - - return (0); + return 0; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); struct vivi_fmt *fmt; enum v4l2_field field; - unsigned int maxw, maxh; fmt = get_format(f); if (!fmt) { @@ -928,113 +806,109 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - maxw = norm_maxw(); - maxh = norm_maxh(); - f->fmt.pix.field = field; - v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, - &f->fmt.pix.height, 32, maxh, 0, 0); + v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2, + &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; } -/*FIXME: This seems to be generic enough to be at videodev2 */ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vivi_fh *fh = priv; - struct videobuf_queue *q = &fh->vb_vidq; + struct vivi_dev *dev = video_drvdata(file); + struct videobuf_queue *q = &dev->vb_vidq; - int ret = vidioc_try_fmt_vid_cap(file, fh, f); + int ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; mutex_lock(&q->vb_lock); - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dprintk(fh->dev, 1, "%s queue busy\n", __func__); + if (vivi_is_generating(dev)) { + dprintk(dev, 1, "%s device busy\n", __func__); ret = -EBUSY; goto out; } - fh->fmt = get_format(f); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - + dev->fmt = get_format(f); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->vb_vidq.field = f->fmt.pix.field; ret = 0; out: mutex_unlock(&q->vb_lock); - return ret; } static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_reqbufs(&fh->vb_vidq, p)); + return videobuf_reqbufs(&dev->vb_vidq, p); } static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_querybuf(&fh->vb_vidq, p)); + return videobuf_querybuf(&dev->vb_vidq, p); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_qbuf(&fh->vb_vidq, p)); + return videobuf_qbuf(&dev->vb_vidq, p); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_dqbuf(&fh->vb_vidq, p, - file->f_flags & O_NONBLOCK)); + return videobuf_dqbuf(&dev->vb_vidq, p, + file->f_flags & O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + return videobuf_cgmbuf(&dev->vb_vidq, mbuf, 8); } #endif static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); + int ret; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + ret = videobuf_streamon(&dev->vb_vidq); + if (ret) + return ret; - return videobuf_streamon(&fh->vb_vidq); + vivi_start_generating(file); + return 0; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); + int ret; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - - return videobuf_streamoff(&fh->vb_vidq); + ret = videobuf_streamoff(&dev->vb_vidq); + if (!ret) + vivi_stop_generating(file); + return ret; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) @@ -1052,80 +926,104 @@ static int vidioc_enum_input(struct file *file, void *priv, inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = V4L2_STD_525_60; sprintf(inp->name, "Camera %u", inp->index); - - return (0); + return 0; } static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); *i = dev->input; - - return (0); + return 0; } + static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); if (i >= NUM_INPUTS) return -EINVAL; dev->input = i; - precalculate_bars(fh); - - return (0); + precalculate_bars(dev); + precalculate_line(dev); + return 0; } - /* --- controls ---------------------------------------------- */ +/* --- controls ---------------------------------------------- */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { - int i; - - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - if (qc->id && qc->id == vivi_qctrl[i].id) { - memcpy(qc, &(vivi_qctrl[i]), - sizeof(*qc)); - return (0); - } - + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 200); + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 16); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); + } return -EINVAL; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - if (ctrl->id == vivi_qctrl[i].id) { - ctrl->value = dev->qctl_regs[i]; - return 0; - } + struct vivi_dev *dev = video_drvdata(file); + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = dev->volume; + return 0; + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->brightness; + return 0; + case V4L2_CID_CONTRAST: + ctrl->value = dev->contrast; + return 0; + case V4L2_CID_SATURATION: + ctrl->value = dev->saturation; + return 0; + case V4L2_CID_HUE: + ctrl->value = dev->hue; + return 0; + } return -EINVAL; } + static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - if (ctrl->id == vivi_qctrl[i].id) { - if (ctrl->value < vivi_qctrl[i].minimum || - ctrl->value > vivi_qctrl[i].maximum) { - return -ERANGE; - } - dev->qctl_regs[i] = ctrl->value; - return 0; - } + struct vivi_dev *dev = video_drvdata(file); + struct v4l2_queryctrl qc; + int err; + + qc.id = ctrl->id; + err = vidioc_queryctrl(file, priv, &qc); + if (err < 0) + return err; + if (ctrl->value < qc.minimum || ctrl->value > qc.maximum) + return -ERANGE; + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + return 0; + case V4L2_CID_BRIGHTNESS: + dev->brightness = ctrl->value; + return 0; + case V4L2_CID_CONTRAST: + dev->contrast = ctrl->value; + return 0; + case V4L2_CID_SATURATION: + dev->saturation = ctrl->value; + return 0; + case V4L2_CID_HUE: + dev->hue = ctrl->value; + return 0; + } return -EINVAL; } @@ -1133,134 +1031,58 @@ static int vidioc_s_ctrl(struct file *file, void *priv, File operations for the device ------------------------------------------------------------------*/ -static int vivi_open(struct file *file) -{ - struct vivi_dev *dev = video_drvdata(file); - struct vivi_fh *fh = NULL; - int retval = 0; - - mutex_lock(&dev->mutex); - dev->users++; - - if (dev->users > 1) { - dev->users--; - mutex_unlock(&dev->mutex); - return -EBUSY; - } - - dprintk(dev, 1, "open %s type=%s users=%d\n", - video_device_node_name(dev->vfd), - v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - dev->users--; - retval = -ENOMEM; - } - mutex_unlock(&dev->mutex); - - if (retval) - return retval; - - file->private_data = fh; - fh->dev = dev; - - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->fmt = &formats[0]; - fh->width = 640; - fh->height = 480; - - /* Resets frame counters */ - dev->h = 0; - dev->m = 0; - dev->s = 0; - dev->ms = 0; - dev->mv_count = 0; - dev->jiffies = jiffies; - sprintf(dev->timestr, "%02d:%02d:%02d:%03d", - dev->h, dev->m, dev->s, dev->ms); - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &vivi_video_qops, - NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct vivi_buffer), fh); - - vivi_start_thread(fh); - - return 0; -} - static ssize_t vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct vivi_fh *fh = file->private_data; + struct vivi_dev *dev = video_drvdata(file); - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, + vivi_start_generating(file); + return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0, file->f_flags & O_NONBLOCK); - } - return 0; } static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { - struct vivi_fh *fh = file->private_data; - struct vivi_dev *dev = fh->dev; - struct videobuf_queue *q = &fh->vb_vidq; + struct vivi_dev *dev = video_drvdata(file); + struct videobuf_queue *q = &dev->vb_vidq; dprintk(dev, 1, "%s\n", __func__); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; - + vivi_start_generating(file); return videobuf_poll_stream(file, q, wait); } static int vivi_close(struct file *file) { - struct vivi_fh *fh = file->private_data; - struct vivi_dev *dev = fh->dev; - struct vivi_dmaqueue *vidq = &dev->vidq; struct video_device *vdev = video_devdata(file); + struct vivi_dev *dev = video_drvdata(file); - vivi_stop_thread(vidq); - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - kfree(fh); - - mutex_lock(&dev->mutex); - dev->users--; - mutex_unlock(&dev->mutex); - - dprintk(dev, 1, "close called (dev=%s, users=%d)\n", - video_device_node_name(vdev), dev->users); + vivi_stop_generating(file); + dprintk(dev, 1, "close called (dev=%s)\n", + video_device_node_name(vdev)); return 0; } static int vivi_mmap(struct file *file, struct vm_area_struct *vma) { - struct vivi_fh *fh = file->private_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); int ret; dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); - ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + ret = videobuf_mmap_mapper(&dev->vb_vidq, vma); dprintk(dev, 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, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; } static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, - .open = vivi_open, .release = vivi_close, .read = vivi_read, .poll = vivi_poll, @@ -1282,11 +1104,11 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif @@ -1330,7 +1152,7 @@ static int __init vivi_create_instance(int inst) { struct vivi_dev *dev; struct video_device *vfd; - int ret, i; + int ret; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) @@ -1342,6 +1164,20 @@ static int __init vivi_create_instance(int inst) if (ret) goto free_dev; + dev->fmt = &formats[0]; + dev->width = 640; + dev->height = 480; + dev->volume = 200; + dev->brightness = 127; + dev->contrast = 16; + dev->saturation = 127; + dev->hue = 0; + + videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops, + NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct vivi_buffer), dev); + /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); init_waitqueue_head(&dev->vidq.wq); @@ -1357,6 +1193,7 @@ static int __init vivi_create_instance(int inst) *vfd = vivi_template; vfd->debug = debug; + vfd->v4l2_dev = &dev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); if (ret < 0) @@ -1364,10 +1201,6 @@ static int __init vivi_create_instance(int inst) video_set_drvdata(vfd, dev); - /* Set all controls to their default value. */ - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - dev->qctl_regs[i] = vivi_qctrl[i].default_value; - /* Now that everything is fine, let's add it to device list */ list_add_tail(&dev->vivi_devlist, &vivi_devlist); @@ -1396,8 +1229,15 @@ free_dev: */ static int __init vivi_init(void) { + const struct font_desc *font = find_font("VGA8x16"); int ret = 0, i; + if (font == NULL) { + printk(KERN_ERR "vivi: could not find font\n"); + return -ENODEV; + } + font8x16 = font->data; + if (n_devs <= 0) n_devs = 1; @@ -1412,7 +1252,7 @@ static int __init vivi_init(void) } if (ret < 0) { - printk(KERN_INFO "Error %d while loading vivi driver\n", ret); + printk(KERN_ERR "vivi: error %d while loading driver\n", ret); return ret; } diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index bf9bf650a31..635420d8d84 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -1,7 +1,7 @@ /* Winbond w9966cf Webcam parport driver. - Version 0.32 + Version 0.33 Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se> @@ -57,10 +57,12 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/videodev.h> +#include <linux/version.h> +#include <linux/videodev2.h> #include <linux/slab.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <linux/parport.h> /*#define DEBUG*/ /* Undef me for production */ @@ -76,12 +78,12 @@ */ #define W9966_DRIVERNAME "W9966CF Webcam" -#define W9966_MAXCAMS 4 // Maximum number of cameras -#define W9966_RBUFFER 2048 // Read buffer (must be an even number) -#define W9966_SRAMSIZE 131072 // 128kb -#define W9966_SRAMID 0x02 // check w9966cf.pdf +#define W9966_MAXCAMS 4 /* Maximum number of cameras */ +#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */ +#define W9966_SRAMSIZE 131072 /* 128kb */ +#define W9966_SRAMID 0x02 /* check w9966cf.pdf */ -// Empirically determined window limits +/* Empirically determined window limits */ #define W9966_WND_MIN_X 16 #define W9966_WND_MIN_Y 14 #define W9966_WND_MAX_X 705 @@ -89,7 +91,7 @@ #define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X) #define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y) -// Keep track of our current state +/* Keep track of our current state */ #define W9966_STATE_PDEV 0x01 #define W9966_STATE_CLAIMED 0x02 #define W9966_STATE_VDEV 0x04 @@ -101,12 +103,13 @@ #define W9966_I2C_W_DATA 0x02 #define W9966_I2C_W_CLOCK 0x01 -struct w9966_dev { +struct w9966 { + struct v4l2_device v4l2_dev; unsigned char dev_state; unsigned char i2c_state; unsigned short ppmode; - struct parport* pport; - struct pardevice* pdev; + struct parport *pport; + struct pardevice *pdev; struct video_device vdev; unsigned short width; unsigned short height; @@ -114,7 +117,7 @@ struct w9966_dev { signed char contrast; signed char color; signed char hue; - unsigned long in_use; + struct mutex lock; }; /* @@ -127,15 +130,15 @@ MODULE_LICENSE("GPL"); #ifdef MODULE -static const char* pardev[] = {[0 ... W9966_MAXCAMS] = ""}; +static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; #else -static const char* pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; +static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; #endif module_param_array(pardev, charp, NULL, 0); -MODULE_PARM_DESC(pardev, "pardev: where to search for\n\ -\teach camera. 'aggressive' means brute-force search.\n\ -\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n\ -\tcam 1 to parport3 and search every parport for cam 2 etc..."); +MODULE_PARM_DESC(pardev, "pardev: where to search for\n" + "\teach camera. 'aggressive' means brute-force search.\n" + "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n" + "\tcam 1 to parport3 and search every parport for cam 2 etc..."); static int parmode; module_param(parmode, int, 0); @@ -144,117 +147,49 @@ MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); static int video_nr = -1; module_param(video_nr, int, 0); -/* - * Private data - */ - -static struct w9966_dev w9966_cams[W9966_MAXCAMS]; - -/* - * Private function declares - */ - -static inline void w9966_setState(struct w9966_dev* cam, int mask, int val); -static inline int w9966_getState(struct w9966_dev* cam, int mask, int val); -static inline void w9966_pdev_claim(struct w9966_dev *vdev); -static inline void w9966_pdev_release(struct w9966_dev *vdev); - -static int w9966_rReg(struct w9966_dev* cam, int reg); -static int w9966_wReg(struct w9966_dev* cam, int reg, int data); -#if 0 -static int w9966_rReg_i2c(struct w9966_dev* cam, int reg); -#endif -static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data); -static int w9966_findlen(int near, int size, int maxlen); -static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor); -static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h); - -static int w9966_init(struct w9966_dev* cam, struct parport* port); -static void w9966_term(struct w9966_dev* cam); - -static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state); -static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state); -static inline int w9966_i2c_getsda(struct w9966_dev* cam); -static inline int w9966_i2c_getscl(struct w9966_dev* cam); -static int w9966_i2c_wbyte(struct w9966_dev* cam, int data); -#if 0 -static int w9966_i2c_rbyte(struct w9966_dev* cam); -#endif - -static long w9966_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t w9966_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); - -static int w9966_exclusive_open(struct file *file) -{ - struct w9966_dev *cam = video_drvdata(file); - - return test_and_set_bit(0, &cam->in_use) ? -EBUSY : 0; -} - -static int w9966_exclusive_release(struct file *file) -{ - struct w9966_dev *cam = video_drvdata(file); - - clear_bit(0, &cam->in_use); - return 0; -} - -static const struct v4l2_file_operations w9966_fops = { - .owner = THIS_MODULE, - .open = w9966_exclusive_open, - .release = w9966_exclusive_release, - .ioctl = w9966_v4l_ioctl, - .read = w9966_v4l_read, -}; -static struct video_device w9966_template = { - .name = W9966_DRIVERNAME, - .fops = &w9966_fops, - .release = video_device_release_empty, -}; +static struct w9966 w9966_cams[W9966_MAXCAMS]; /* * Private function defines */ -// Set camera phase flags, so we know what to uninit when terminating -static inline void w9966_setState(struct w9966_dev* cam, int mask, int val) +/* Set camera phase flags, so we know what to uninit when terminating */ +static inline void w9966_set_state(struct w9966 *cam, int mask, int val) { cam->dev_state = (cam->dev_state & ~mask) ^ val; } -// Get camera phase flags -static inline int w9966_getState(struct w9966_dev* cam, int mask, int val) +/* Get camera phase flags */ +static inline int w9966_get_state(struct w9966 *cam, int mask, int val) { return ((cam->dev_state & mask) == val); } -// Claim parport for ourself -static inline void w9966_pdev_claim(struct w9966_dev* cam) +/* Claim parport for ourself */ +static void w9966_pdev_claim(struct w9966 *cam) { - if (w9966_getState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) + if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) return; parport_claim_or_block(cam->pdev); - w9966_setState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); + w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); } -// Release parport for others to use -static inline void w9966_pdev_release(struct w9966_dev* cam) +/* Release parport for others to use */ +static void w9966_pdev_release(struct w9966 *cam) { - if (w9966_getState(cam, W9966_STATE_CLAIMED, 0)) + if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0)) return; parport_release(cam->pdev); - w9966_setState(cam, W9966_STATE_CLAIMED, 0); + w9966_set_state(cam, W9966_STATE_CLAIMED, 0); } -// Read register from W9966 interface-chip -// Expects a claimed pdev -// -1 on error, else register data (byte) -static int w9966_rReg(struct w9966_dev* cam, int reg) +/* Read register from W9966 interface-chip + Expects a claimed pdev + -1 on error, else register data (byte) */ +static int w9966_read_reg(struct w9966 *cam, int reg) { - // ECP, read, regtransfer, REG, REG, REG, REG, REG + /* ECP, read, regtransfer, REG, REG, REG, REG, REG */ const unsigned char addr = 0x80 | (reg & 0x1f); unsigned char val; @@ -270,12 +205,12 @@ static int w9966_rReg(struct w9966_dev* cam, int reg) return val; } -// Write register to W9966 interface-chip -// Expects a claimed pdev -// -1 on error -static int w9966_wReg(struct w9966_dev* cam, int reg, int data) +/* Write register to W9966 interface-chip + Expects a claimed pdev + -1 on error */ +static int w9966_write_reg(struct w9966 *cam, int reg, int data) { - // ECP, write, regtransfer, REG, REG, REG, REG, REG + /* ECP, write, regtransfer, REG, REG, REG, REG, REG */ const unsigned char addr = 0xc0 | (reg & 0x1f); const unsigned char val = data; @@ -291,119 +226,186 @@ static int w9966_wReg(struct w9966_dev* cam, int reg, int data) return 0; } -// Initialize camera device. Setup all internal flags, set a -// default video mode, setup ccd-chip, register v4l device etc.. -// Also used for 'probing' of hardware. -// -1 on error -static int w9966_init(struct w9966_dev* cam, struct parport* port) +/* + * Ugly and primitive i2c protocol functions + */ + +/* Sets the data line on the i2c bus. + Expects a claimed pdev. */ +static void w9966_i2c_setsda(struct w9966 *cam, int state) { - if (cam->dev_state != 0) - return -1; + if (state) + cam->i2c_state |= W9966_I2C_W_DATA; + else + cam->i2c_state &= ~W9966_I2C_W_DATA; - cam->pport = port; - cam->brightness = 128; - cam->contrast = 64; - cam->color = 64; - cam->hue = 0; + w9966_write_reg(cam, 0x18, cam->i2c_state); + udelay(5); +} -// Select requested transfer mode - switch(parmode) - { - default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) - case 0: - if (port->modes & PARPORT_MODE_ECP) - cam->ppmode = IEEE1284_MODE_ECP; - else if (port->modes & PARPORT_MODE_EPP) - cam->ppmode = IEEE1284_MODE_EPP; - else - cam->ppmode = IEEE1284_MODE_ECP; - break; - case 1: // hw- or sw-ecp - cam->ppmode = IEEE1284_MODE_ECP; - break; - case 2: // hw- or sw-epp - cam->ppmode = IEEE1284_MODE_EPP; - break; +/* Get peripheral clock line + Expects a claimed pdev. */ +static int w9966_i2c_getscl(struct w9966 *cam) +{ + const unsigned char state = w9966_read_reg(cam, 0x18); + return ((state & W9966_I2C_R_CLOCK) > 0); +} + +/* Sets the clock line on the i2c bus. + Expects a claimed pdev. -1 on error */ +static int w9966_i2c_setscl(struct w9966 *cam, int state) +{ + unsigned long timeout; + + if (state) + cam->i2c_state |= W9966_I2C_W_CLOCK; + else + cam->i2c_state &= ~W9966_I2C_W_CLOCK; + + w9966_write_reg(cam, 0x18, cam->i2c_state); + udelay(5); + + /* we go to high, we also expect the peripheral to ack. */ + if (state) { + timeout = jiffies + 100; + while (!w9966_i2c_getscl(cam)) { + if (time_after(jiffies, timeout)) + return -1; + } } + return 0; +} -// Tell the parport driver that we exists - cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); - if (cam->pdev == NULL) { - DPRINTF("parport_register_device() failed\n"); - return -1; +#if 0 +/* Get peripheral data line + Expects a claimed pdev. */ +static int w9966_i2c_getsda(struct w9966 *cam) +{ + const unsigned char state = w9966_read_reg(cam, 0x18); + return ((state & W9966_I2C_R_DATA) > 0); +} +#endif + +/* Write a byte with ack to the i2c bus. + Expects a claimed pdev. -1 on error */ +static int w9966_i2c_wbyte(struct w9966 *cam, int data) +{ + int i; + + for (i = 7; i >= 0; i--) { + w9966_i2c_setsda(cam, (data >> i) & 0x01); + + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setscl(cam, 0); } - w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); - w9966_pdev_claim(cam); + w9966_i2c_setsda(cam, 1); -// Setup a default capture mode - if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { - DPRINTF("w9966_setup() failed.\n"); + if (w9966_i2c_setscl(cam, 1) == -1) return -1; + w9966_i2c_setscl(cam, 0); + + return 0; +} + +/* Read a data byte with ack from the i2c-bus + Expects a claimed pdev. -1 on error */ +#if 0 +static int w9966_i2c_rbyte(struct w9966 *cam) +{ + unsigned char data = 0x00; + int i; + + w9966_i2c_setsda(cam, 1); + + for (i = 0; i < 8; i++) { + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + data = data << 1; + if (w9966_i2c_getsda(cam)) + data |= 0x01; + + w9966_i2c_setscl(cam, 0); } + return data; +} +#endif - w9966_pdev_release(cam); +/* Read a register from the i2c device. + Expects claimed pdev. -1 on error */ +#if 0 +static int w9966_read_reg_i2c(struct w9966 *cam, int reg) +{ + int data; -// Fill in the video_device struct and register us to v4l - memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device)); - video_set_drvdata(&cam->vdev, cam); + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || + w9966_i2c_wbyte(cam, reg) == -1) + return -1; + + w9966_i2c_setsda(cam, 1); + if (w9966_i2c_setscl(cam, 1) == -1) return -1; + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); - w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); + if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1) + return -1; + data = w9966_i2c_rbyte(cam); + if (data == -1) + return -1; - // All ok - printk( - "w9966cf: Found and initialized a webcam on %s.\n", - cam->pport->name - ); - return 0; -} + w9966_i2c_setsda(cam, 0); + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setsda(cam, 1); -// Terminate everything gracefully -static void w9966_term(struct w9966_dev* cam) + return data; +} +#endif + +/* Write a register to the i2c device. + Expects claimed pdev. -1 on error */ +static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data) { -// Unregister from v4l - if (w9966_getState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { - video_unregister_device(&cam->vdev); - w9966_setState(cam, W9966_STATE_VDEV, 0); - } + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); -// Terminate from IEEE1284 mode and release pdev block - if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { - w9966_pdev_claim(cam); - parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); - w9966_pdev_release(cam); - } + if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || + w9966_i2c_wbyte(cam, reg) == -1 || + w9966_i2c_wbyte(cam, data) == -1) + return -1; -// Unregister from parport - if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { - parport_unregister_device(cam->pdev); - w9966_setState(cam, W9966_STATE_PDEV, 0); - } -} + w9966_i2c_setsda(cam, 0); + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + + w9966_i2c_setsda(cam, 1); + return 0; +} -// Find a good length for capture window (used both for W and H) -// A bit ugly but pretty functional. The capture length -// have to match the downscale +/* Find a good length for capture window (used both for W and H) + A bit ugly but pretty functional. The capture length + have to match the downscale */ static int w9966_findlen(int near, int size, int maxlen) { int bestlen = size; int besterr = abs(near - bestlen); int len; - for(len = size+1;len < maxlen;len++) - { + for (len = size + 1; len < maxlen; len++) { int err; - if ( ((64*size) %len) != 0) + if (((64 * size) % len) != 0) continue; err = abs(near - len); - // Only continue as long as we keep getting better values + /* Only continue as long as we keep getting better values */ if (err > besterr) break; @@ -414,32 +416,32 @@ static int w9966_findlen(int near, int size, int maxlen) return bestlen; } -// Modify capture window (if necessary) -// and calculate downscaling -// Return -1 on error -static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor) +/* Modify capture window (if necessary) + and calculate downscaling + Return -1 on error */ +static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor) { int maxlen = max - min; int len = *end - *beg + 1; int newlen = w9966_findlen(len, size, maxlen); int err = newlen - len; - // Check for bad format + /* Check for bad format */ if (newlen > maxlen || newlen < size) return -1; - // Set factor (6 bit fixed) - *factor = (64*size) / newlen; + /* Set factor (6 bit fixed) */ + *factor = (64 * size) / newlen; if (*factor == 64) - *factor = 0x00; // downscale is disabled + *factor = 0x00; /* downscale is disabled */ else - *factor |= 0x80; // set downscale-enable bit + *factor |= 0x80; /* set downscale-enable bit */ - // Modify old beginning and end + /* Modify old beginning and end */ *beg -= err / 2; *end += err - (err / 2); - // Move window if outside borders + /* Move window if outside borders */ if (*beg < min) { *end += min - *beg; *beg += min - *beg; @@ -452,10 +454,10 @@ static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsig return 0; } -// Setup the cameras capture window etc. -// Expects a claimed pdev -// return -1 on error -static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h) +/* Setup the cameras capture window etc. + Expects a claimed pdev + return -1 on error */ +static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h) { unsigned int i; unsigned int enh_s, enh_e; @@ -469,443 +471,314 @@ static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, in }; - if (w*h*2 > W9966_SRAMSIZE) - { + if (w * h * 2 > W9966_SRAMSIZE) { DPRINTF("capture window exceeds SRAM size!.\n"); - w = 200; h = 160; // Pick default values + w = 200; h = 160; /* Pick default values */ } w &= ~0x1; - if (w < 2) w = 2; - if (h < 1) h = 1; - if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W; - if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H; + if (w < 2) + w = 2; + if (h < 1) + h = 1; + if (w > W9966_WND_MAX_W) + w = W9966_WND_MAX_W; + if (h > W9966_WND_MAX_H) + h = W9966_WND_MAX_H; cam->width = w; cam->height = h; enh_s = 0; - enh_e = w*h*2; - -// Modify capture window if necessary and calculate downscaling - if ( - w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || - w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0 - ) return -1; - - DPRINTF( - "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", - w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80 - ); - -// Setup registers - regs[0x00] = 0x00; // Set normal operation - regs[0x01] = 0x18; // Capture mode - regs[0x02] = scale_y; // V-scaling - regs[0x03] = scale_x; // H-scaling - - // Capture window - regs[0x04] = (x1 & 0x0ff); // X-start (8 low bits) - regs[0x05] = (x1 & 0x300)>>8; // X-start (2 high bits) - regs[0x06] = (y1 & 0x0ff); // Y-start (8 low bits) - regs[0x07] = (y1 & 0x300)>>8; // Y-start (2 high bits) - regs[0x08] = (x2 & 0x0ff); // X-end (8 low bits) - regs[0x09] = (x2 & 0x300)>>8; // X-end (2 high bits) - regs[0x0a] = (y2 & 0x0ff); // Y-end (8 low bits) - - regs[0x0c] = W9966_SRAMID; // SRAM-banks (1x 128kb) - - // Enhancement layer - regs[0x0d] = (enh_s& 0x000ff); // Enh. start (0-7) - regs[0x0e] = (enh_s& 0x0ff00)>>8; // Enh. start (8-15) - regs[0x0f] = (enh_s& 0x70000)>>16; // Enh. start (16-17/18??) - regs[0x10] = (enh_e& 0x000ff); // Enh. end (0-7) - regs[0x11] = (enh_e& 0x0ff00)>>8; // Enh. end (8-15) - regs[0x12] = (enh_e& 0x70000)>>16; // Enh. end (16-17/18??) - - // Misc - regs[0x13] = 0x40; // VEE control (raw 4:2:2) - regs[0x17] = 0x00; // ??? - regs[0x18] = cam->i2c_state = 0x00; // Serial bus - regs[0x19] = 0xff; // I/O port direction control - regs[0x1a] = 0xff; // I/O port data register - regs[0x1b] = 0x10; // ??? - - // SAA7111 chip settings + enh_e = w * h * 2; + + /* Modify capture window if necessary and calculate downscaling */ + if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || + w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0) + return -1; + + DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", + w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80); + + /* Setup registers */ + regs[0x00] = 0x00; /* Set normal operation */ + regs[0x01] = 0x18; /* Capture mode */ + regs[0x02] = scale_y; /* V-scaling */ + regs[0x03] = scale_x; /* H-scaling */ + + /* Capture window */ + regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */ + regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */ + regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */ + regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */ + regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */ + regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */ + regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */ + + regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */ + + /* Enhancement layer */ + regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */ + regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */ + regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */ + regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */ + regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */ + regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */ + + /* Misc */ + regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */ + regs[0x17] = 0x00; /* ??? */ + regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */ + regs[0x19] = 0xff; /* I/O port direction control */ + regs[0x1a] = 0xff; /* I/O port data register */ + regs[0x1b] = 0x10; /* ??? */ + + /* SAA7111 chip settings */ saa7111_regs[0x0a] = cam->brightness; saa7111_regs[0x0b] = cam->contrast; saa7111_regs[0x0c] = cam->color; saa7111_regs[0x0d] = cam->hue; -// Reset (ECP-fifo & serial-bus) - if (w9966_wReg(cam, 0x00, 0x03) == -1) + /* Reset (ECP-fifo & serial-bus) */ + if (w9966_write_reg(cam, 0x00, 0x03) == -1) return -1; -// Write regs to w9966cf chip + /* Write regs to w9966cf chip */ for (i = 0; i < 0x1c; i++) - if (w9966_wReg(cam, i, regs[i]) == -1) + if (w9966_write_reg(cam, i, regs[i]) == -1) return -1; -// Write regs to saa7111 chip + /* Write regs to saa7111 chip */ for (i = 0; i < 0x20; i++) - if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1) + if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1) return -1; return 0; } /* - * Ugly and primitive i2c protocol functions + * Video4linux interfacing */ -// Sets the data line on the i2c bus. -// Expects a claimed pdev. -static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state) +static int cam_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - if (state) - cam->i2c_state |= W9966_I2C_W_DATA; - else - cam->i2c_state &= ~W9966_I2C_W_DATA; + struct w9966 *cam = video_drvdata(file); - w9966_wReg(cam, 0x18, cam->i2c_state); - udelay(5); + strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card)); + strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 33, 0); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; } -// Get peripheral clock line -// Expects a claimed pdev. -static inline int w9966_i2c_getscl(struct w9966_dev* cam) +static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) { - const unsigned char state = w9966_rReg(cam, 0x18); - return ((state & W9966_I2C_R_CLOCK) > 0); + if (vin->index > 0) + return -EINVAL; + strlcpy(vin->name, "Camera", sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = 0; + vin->status = 0; + return 0; } -// Sets the clock line on the i2c bus. -// Expects a claimed pdev. -1 on error -static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state) +static int cam_g_input(struct file *file, void *fh, unsigned int *inp) { - unsigned long timeout; - - if (state) - cam->i2c_state |= W9966_I2C_W_CLOCK; - else - cam->i2c_state &= ~W9966_I2C_W_CLOCK; - - w9966_wReg(cam, 0x18, cam->i2c_state); - udelay(5); - - // we go to high, we also expect the peripheral to ack. - if (state) { - timeout = jiffies + 100; - while (!w9966_i2c_getscl(cam)) { - if (time_after(jiffies, timeout)) - return -1; - } - } + *inp = 0; return 0; } -// Get peripheral data line -// Expects a claimed pdev. -static inline int w9966_i2c_getsda(struct w9966_dev* cam) +static int cam_s_input(struct file *file, void *fh, unsigned int inp) { - const unsigned char state = w9966_rReg(cam, 0x18); - return ((state & W9966_I2C_R_DATA) > 0); + return (inp > 0) ? -EINVAL : 0; } -// Write a byte with ack to the i2c bus. -// Expects a claimed pdev. -1 on error -static int w9966_i2c_wbyte(struct w9966_dev* cam, int data) +static int cam_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) { - int i; - for (i = 7; i >= 0; i--) - { - w9966_i2c_setsda(cam, (data >> i) & 0x01); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setscl(cam, 0); + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); } - - w9966_i2c_setsda(cam, 1); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setscl(cam, 0); - - return 0; + return -EINVAL; } -// Read a data byte with ack from the i2c-bus -// Expects a claimed pdev. -1 on error -#if 0 -static int w9966_i2c_rbyte(struct w9966_dev* cam) +static int cam_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - unsigned char data = 0x00; - int i; - - w9966_i2c_setsda(cam, 1); + struct w9966 *cam = video_drvdata(file); + int ret = 0; - for (i = 0; i < 8; i++) - { - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - data = data << 1; - if (w9966_i2c_getsda(cam)) - data |= 0x01; - - w9966_i2c_setscl(cam, 0); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = cam->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = cam->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = cam->color; + break; + case V4L2_CID_HUE: + ctrl->value = cam->hue; + break; + default: + ret = -EINVAL; + break; } - return data; + return ret; } -#endif -// Read a register from the i2c device. -// Expects claimed pdev. -1 on error -#if 0 -static int w9966_rReg_i2c(struct w9966_dev* cam, int reg) +static int cam_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - int data; - - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if ( - w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || - w9966_i2c_wbyte(cam, reg) == -1 - ) - return -1; - - w9966_i2c_setsda(cam, 1); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); + struct w9966 *cam = video_drvdata(file); + int ret = 0; - if ( - w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1 || - (data = w9966_i2c_rbyte(cam)) == -1 - ) - return -1; + mutex_lock(&cam->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cam->brightness = ctrl->value; + break; + case V4L2_CID_CONTRAST: + cam->contrast = ctrl->value; + break; + case V4L2_CID_SATURATION: + cam->color = ctrl->value; + break; + case V4L2_CID_HUE: + cam->hue = ctrl->value; + break; + default: + ret = -EINVAL; + break; + } - w9966_i2c_setsda(cam, 0); + if (ret == 0) { + w9966_pdev_claim(cam); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setsda(cam, 1); + if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 || + w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 || + w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 || + w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) { + ret = -EIO; + } - return data; + w9966_pdev_release(cam); + } + mutex_unlock(&cam->lock); + return ret; } -#endif -// Write a register to the i2c device. -// Expects claimed pdev. -1 on error -static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) +static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if ( - w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || - w9966_i2c_wbyte(cam, reg) == -1 || - w9966_i2c_wbyte(cam, data) == -1 - ) - return -1; - - w9966_i2c_setsda(cam, 0); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - - w9966_i2c_setsda(cam, 1); - + struct w9966 *cam = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = cam->width; + pix->height = cam->height; + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * cam->width; + pix->sizeimage = 2 * cam->width * cam->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } -/* - * Video4linux interfacing - */ - -static long w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct w9966_dev *cam = video_drvdata(file); - - switch(cmd) - { - case VIDIOCGCAP: - { - static struct video_capability vcap = { - .name = W9966_DRIVERNAME, - .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, - .channels = 1, - .maxwidth = W9966_WND_MAX_W, - .maxheight = W9966_WND_MAX_H, - .minwidth = 2, - .minheight = 1, - }; - struct video_capability *cap = arg; - *cap = vcap; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *vch = arg; - if(vch->channel != 0) // We only support one channel (#0) - return -EINVAL; - memset(vch,0,sizeof(*vch)); - strcpy(vch->name, "CCD-input"); - vch->type = VIDEO_TYPE_CAMERA; - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *vch = arg; - if(vch->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *vtune = arg; - if(vtune->tuner != 0) - return -EINVAL; - strcpy(vtune->name, "no tuner"); - vtune->rangelow = 0; - vtune->rangehigh = 0; - vtune->flags = VIDEO_TUNER_NORM; - vtune->mode = VIDEO_MODE_AUTO; - vtune->signal = 0xffff; - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *vtune = arg; - if (vtune->tuner != 0) - return -EINVAL; - if (vtune->mode != VIDEO_MODE_AUTO) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture vpic = { - cam->brightness << 8, // brightness - (cam->hue + 128) << 8, // hue - cam->color << 9, // color - cam->contrast << 9, // contrast - 0x8000, // whiteness - 16, VIDEO_PALETTE_YUV422// bpp, palette format - }; - struct video_picture *pic = arg; - *pic = vpic; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *vpic = arg; - if (vpic->depth != 16 || (vpic->palette != VIDEO_PALETTE_YUV422 && vpic->palette != VIDEO_PALETTE_YUYV)) - return -EINVAL; - - cam->brightness = vpic->brightness >> 8; - cam->hue = (vpic->hue >> 8) - 128; - cam->color = vpic->colour >> 9; - cam->contrast = vpic->contrast >> 9; +static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->width < 2) + pix->width = 2; + if (pix->height < 1) + pix->height = 1; + if (pix->width > W9966_WND_MAX_W) + pix->width = W9966_WND_MAX_W; + if (pix->height > W9966_WND_MAX_H) + pix->height = W9966_WND_MAX_H; + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * pix->width; + pix->sizeimage = 2 * pix->width * pix->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} - w9966_pdev_claim(cam); +static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct w9966 *cam = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = cam_try_fmt_vid_cap(file, fh, fmt); - if ( - w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 || - w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 || - w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 || - w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1 - ) { - w9966_pdev_release(cam); - return -EIO; - } + if (ret) + return ret; - w9966_pdev_release(cam); - return 0; - } - case VIDIOCSWIN: - { - int ret; - struct video_window *vwin = arg; - - if (vwin->flags != 0) - return -EINVAL; - if (vwin->clipcount != 0) - return -EINVAL; - if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W) - return -EINVAL; - if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H) - return -EINVAL; - - // Update camera regs - w9966_pdev_claim(cam); - ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height); - w9966_pdev_release(cam); + mutex_lock(&cam->lock); + /* Update camera regs */ + w9966_pdev_claim(cam); + ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height); + w9966_pdev_release(cam); + mutex_unlock(&cam->lock); + return ret; +} - if (ret != 0) { - DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n"); - return -EIO; - } +static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "YUV 4:2:2", V4L2_PIX_FMT_YUYV, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vwin = arg; - memset(vwin, 0, sizeof(*vwin)); - vwin->width = cam->width; - vwin->height = cam->height; - return 0; - } - // Unimplemented - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: + if (fmt->index > 0) return -EINVAL; - default: - return -ENOIOCTLCMD; - } - return 0; -} -static long w9966_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl); + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } -// Capture data +/* Capture data */ static ssize_t w9966_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { - struct w9966_dev *cam = video_drvdata(file); - unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 + struct w9966 *cam = video_drvdata(file); + unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */ unsigned char __user *dest = (unsigned char __user *)buf; unsigned long dleft = count; unsigned char *tbuf; - // Why would anyone want more than this?? + /* Why would anyone want more than this?? */ if (count > cam->width * cam->height * 2) return -EINVAL; + mutex_lock(&cam->lock); w9966_pdev_claim(cam); - w9966_wReg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer - w9966_wReg(cam, 0x00, 0x00); // Return to normal operation - w9966_wReg(cam, 0x01, 0x98); // Enable capture - - // write special capture-addr and negotiate into data transfer - if ( - (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0 )|| - (parport_write(cam->pport, &addr, 1) != 1 )|| - (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0 ) - ) { + w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */ + w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */ + w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */ + + /* write special capture-addr and negotiate into data transfer */ + if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) || + (parport_write(cam->pport, &addr, 1) != 1) || + (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) { w9966_pdev_release(cam); + mutex_unlock(&cam->lock); return -EFAULT; } @@ -915,8 +788,7 @@ static ssize_t w9966_v4l_read(struct file *file, char __user *buf, goto out; } - while(dleft > 0) - { + while (dleft > 0) { unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; if (parport_read(cam->pport, tbuf, tsize) < tsize) { @@ -931,43 +803,167 @@ static ssize_t w9966_v4l_read(struct file *file, char __user *buf, dleft -= tsize; } - w9966_wReg(cam, 0x01, 0x18); // Disable capture + w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */ out: kfree(tbuf); w9966_pdev_release(cam); + mutex_unlock(&cam->lock); return count; } +static const struct v4l2_file_operations w9966_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .read = w9966_v4l_read, +}; + +static const struct v4l2_ioctl_ops w9966_ioctl_ops = { + .vidioc_querycap = cam_querycap, + .vidioc_g_input = cam_g_input, + .vidioc_s_input = cam_s_input, + .vidioc_enum_input = cam_enum_input, + .vidioc_queryctrl = cam_queryctrl, + .vidioc_g_ctrl = cam_g_ctrl, + .vidioc_s_ctrl = cam_s_ctrl, + .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap, +}; + -// Called once for every parport on init +/* Initialize camera device. Setup all internal flags, set a + default video mode, setup ccd-chip, register v4l device etc.. + Also used for 'probing' of hardware. + -1 on error */ +static int w9966_init(struct w9966 *cam, struct parport *port) +{ + struct v4l2_device *v4l2_dev = &cam->v4l2_dev; + + if (cam->dev_state != 0) + return -1; + + strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name)); + + if (v4l2_device_register(NULL, v4l2_dev) < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return -1; + } + cam->pport = port; + cam->brightness = 128; + cam->contrast = 64; + cam->color = 64; + cam->hue = 0; + + /* Select requested transfer mode */ + switch (parmode) { + default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */ + case 0: + if (port->modes & PARPORT_MODE_ECP) + cam->ppmode = IEEE1284_MODE_ECP; + else if (port->modes & PARPORT_MODE_EPP) + cam->ppmode = IEEE1284_MODE_EPP; + else + cam->ppmode = IEEE1284_MODE_ECP; + break; + case 1: /* hw- or sw-ecp */ + cam->ppmode = IEEE1284_MODE_ECP; + break; + case 2: /* hw- or sw-epp */ + cam->ppmode = IEEE1284_MODE_EPP; + break; + } + + /* Tell the parport driver that we exists */ + cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); + if (cam->pdev == NULL) { + DPRINTF("parport_register_device() failed\n"); + return -1; + } + w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); + + w9966_pdev_claim(cam); + + /* Setup a default capture mode */ + if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { + DPRINTF("w9966_setup() failed.\n"); + return -1; + } + + w9966_pdev_release(cam); + + /* Fill in the video_device struct and register us to v4l */ + strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name)); + cam->vdev.v4l2_dev = v4l2_dev; + cam->vdev.fops = &w9966_fops; + cam->vdev.ioctl_ops = &w9966_ioctl_ops; + cam->vdev.release = video_device_release_empty; + video_set_drvdata(&cam->vdev, cam); + + mutex_init(&cam->lock); + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + return -1; + + w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); + + /* All ok */ + v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n", + cam->pport->name); + return 0; +} + + +/* Terminate everything gracefully */ +static void w9966_term(struct w9966 *cam) +{ + /* Unregister from v4l */ + if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { + video_unregister_device(&cam->vdev); + w9966_set_state(cam, W9966_STATE_VDEV, 0); + } + + /* Terminate from IEEE1284 mode and release pdev block */ + if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { + w9966_pdev_claim(cam); + parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); + w9966_pdev_release(cam); + } + + /* Unregister from parport */ + if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { + parport_unregister_device(cam->pdev); + w9966_set_state(cam, W9966_STATE_PDEV, 0); + } +} + + +/* Called once for every parport on init */ static void w9966_attach(struct parport *port) { int i; - for (i = 0; i < W9966_MAXCAMS; i++) - { - if (w9966_cams[i].dev_state != 0) // Cam is already assigned + for (i = 0; i < W9966_MAXCAMS; i++) { + if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */ continue; - if ( - strcmp(pardev[i], "aggressive") == 0 || - strcmp(pardev[i], port->name) == 0 - ) { + if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) { if (w9966_init(&w9966_cams[i], port) != 0) - w9966_term(&w9966_cams[i]); - break; // return + w9966_term(&w9966_cams[i]); + break; /* return */ } } } -// Called once for every parport on termination +/* Called once for every parport on termination */ static void w9966_detach(struct parport *port) { int i; + for (i = 0; i < W9966_MAXCAMS; i++) - if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) - w9966_term(&w9966_cams[i]); + if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) + w9966_term(&w9966_cams[i]); } @@ -977,17 +973,18 @@ static struct parport_driver w9966_ppd = { .detach = w9966_detach, }; -// Module entry point +/* Module entry point */ static int __init w9966_mod_init(void) { int i; + for (i = 0; i < W9966_MAXCAMS; i++) w9966_cams[i].dev_state = 0; return parport_register_driver(&w9966_ppd); } -// Module cleanup +/* Module cleanup */ static void __exit w9966_mod_term(void) { parport_unregister_driver(&w9966_ppd); diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index e44e4b5f3e5..bb51cfb0c64 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1153,7 +1153,7 @@ zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + 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; @@ -1163,7 +1163,9 @@ zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) 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; diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index 3a408de91b9..0be783c203f 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -58,7 +58,7 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); .idProduct = (prod), \ .bInterfaceClass = (intclass) -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#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 */ \ diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h index cb1de7ea197..8997add1248 100644 --- a/drivers/media/video/zoran/zoran.h +++ b/drivers/media/video/zoran/zoran.h @@ -33,6 +33,10 @@ #include <media/v4l2-device.h> +#define ZORAN_VIDMODE_PAL 0 +#define ZORAN_VIDMODE_NTSC 1 +#define ZORAN_VIDMODE_SECAM 2 + struct zoran_requestbuffers { unsigned long count; /* Number of buffers for MJPEG grabbing */ unsigned long size; /* Size PER BUFFER in bytes */ @@ -48,7 +52,7 @@ struct zoran_sync { struct zoran_status { int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ int signal; /* Returned: 1 if valid video signal detected */ - int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int norm; /* Returned: ZORAN_VIDMODE_PAL or ZORAN_VIDMODE_NTSC */ int color; /* Returned: 1 if color signal detected */ }; @@ -62,7 +66,7 @@ struct zoran_params { /* Main control parameters */ int input; /* Input channel: 0 = Composite, 1 = S-VHS */ - int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int norm; /* Norm: ZORAN_VIDMODE_PAL or ZORAN_VIDMODE_NTSC */ int decimation; /* decimation of captured video, * enlargement of video played back. * Valid values are 1, 2, 4 or 0. @@ -131,13 +135,13 @@ struct zoran_params { /* Private IOCTL to set up for displaying MJPEG */ -#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) -#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) -#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) -#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) -#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) -#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) -#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) +#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOC_PRIVATE+0, struct zoran_params) +#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOC_PRIVATE+1, struct zoran_params) +#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOC_PRIVATE+2, struct zoran_requestbuffers) +#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOC_PRIVATE+3, int) +#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOC_PRIVATE+4, int) +#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOC_PRIVATE+5, struct zoran_sync) +#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOC_PRIVATE+6, struct zoran_status) #ifdef __KERNEL__ @@ -401,7 +405,7 @@ struct zoran { spinlock_t spinlock; /* Spinlock */ /* Video for Linux parameters */ - int input; /* card's norm and input - norm=VIDEO_MODE_* */ + int input; /* card's norm and input */ v4l2_std_id norm; /* Current buffer params */ diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index ec41303544e..6f89d0a096e 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -60,7 +60,7 @@ #include <linux/spinlock.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include "videocodec.h" @@ -1549,11 +1549,11 @@ static long zoran_default(struct file *file, void *__fh, int cmd, void *arg) mutex_lock(&zr->resource_lock); if (zr->norm & V4L2_STD_NTSC) - bparams->norm = VIDEO_MODE_NTSC; - else if (zr->norm & V4L2_STD_PAL) - bparams->norm = VIDEO_MODE_PAL; + bparams->norm = ZORAN_VIDMODE_NTSC; + else if (zr->norm & V4L2_STD_SECAM) + bparams->norm = ZORAN_VIDMODE_SECAM; else - bparams->norm = VIDEO_MODE_SECAM; + bparams->norm = ZORAN_VIDMODE_PAL; bparams->input = zr->input; @@ -1789,11 +1789,11 @@ gstat_unlock_and_return: bstat->signal = (status & V4L2_IN_ST_NO_SIGNAL) ? 0 : 1; if (norm & V4L2_STD_NTSC) - bstat->norm = VIDEO_MODE_NTSC; + bstat->norm = ZORAN_VIDMODE_NTSC; else if (norm & V4L2_STD_SECAM) - bstat->norm = VIDEO_MODE_SECAM; + bstat->norm = ZORAN_VIDMODE_SECAM; else - bstat->norm = VIDEO_MODE_PAL; + bstat->norm = ZORAN_VIDMODE_PAL; bstat->color = (status & V4L2_IN_ST_NO_COLOR) ? 0 : 1; diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 3d4bac25290..a82b5bd18d2 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -376,8 +376,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, if (*count == 0) *count = ZR364XX_DEF_BUFS; - while (*size * (*count) > ZR364XX_DEF_BUFS * 1024 * 1024) - (*count)--; + if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) + *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; return 0; } |