diff options
Diffstat (limited to 'drivers/media/video')
177 files changed, 9833 insertions, 3396 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9adada0..ce1e7ba 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -273,6 +273,16 @@ config VIDEO_ADV7180 To compile this driver as a module, choose M here: the module will be called adv7180. +config VIDEO_ADV7183 + tristate "Analog Devices ADV7183 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + V4l2 subdevice driver for the Analog Devices + ADV7183 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7183. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C @@ -459,6 +469,9 @@ config VIDEO_AK881X comment "Camera sensor devices" +config VIDEO_APTINA_PLL + tristate + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 @@ -467,9 +480,28 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_VS6624 + tristate "ST VS6624 sensor support" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the ST VS6624 + camera. + + To compile this driver as a module, choose M here: the + module will be called vs6624. + +config VIDEO_MT9M032 + tristate "MT9M032 camera sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEO_APTINA_PLL + ---help--- + This driver supports MT9M032 camera sensors from Aptina, monochrome + models only. + config VIDEO_MT9P031 tristate "Aptina MT9P031 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEO_APTINA_PLL ---help--- This is a Video4Linux2 sensor-level driver for the Aptina (Micron) mt9p031 5 Mpixel camera. @@ -851,6 +883,8 @@ source "drivers/media/video/davinci/Kconfig" source "drivers/media/video/omap/Kconfig" +source "drivers/media/video/blackfin/Kconfig" + config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on VIDEO_DEV && ARCH_SHMOBILE @@ -1087,7 +1121,7 @@ config VIDEO_MX2_HOSTSUPPORT config VIDEO_MX2 tristate "i.MX27/i.MX25 Camera Sensor Interface driver" depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25) - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG select VIDEO_MX2_HOSTSUPPORT ---help--- This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor @@ -1116,7 +1150,8 @@ config VIDEO_ATMEL_ISI config VIDEO_S5P_MIPI_CSIS tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" - depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P + depends on VIDEO_V4L2_SUBDEV_API && REGULATOR ---help--- This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. @@ -1176,4 +1211,14 @@ config VIDEO_SAMSUNG_S5P_MFC help MFC 5.1 driver for V4L2. +config VIDEO_MX2_EMMAPRP + tristate "MX2 eMMa-PrP support" + depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27 + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + MX2X chips have a PrP that can be used to process buffers from + memory to memory. Operations include resizing and format + conversion. + endif # V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 3541388..a6282a3 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -12,16 +12,19 @@ omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o +ifeq ($(CONFIG_COMPAT),y) + videodev-objs += v4l2-compat-ioctl32.o +endif # V4L2 core modules obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o -ifeq ($(CONFIG_COMPAT),y) - obj-$(CONFIG_VIDEO_DEV) += v4l2-compat-ioctl32.o -endif - obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o +# Helper modules + +obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o + # All i2c modules must come first: obj-$(CONFIG_VIDEO_TUNER) += tuner.o @@ -40,8 +43,10 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o +obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o obj-$(CONFIG_VIDEO_BT866) += bt866.o @@ -65,6 +70,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o +obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o @@ -177,6 +183,8 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o +obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o + obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ @@ -184,6 +192,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ +obj-$(CONFIG_BLACKFIN) += blackfin/ + obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o @@ -199,6 +209,6 @@ obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/common/tuners diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index 12eedf4..5b045b4 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -33,7 +33,6 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/i2c.h> -#include <linux/module.h> #include <linux/slab.h> #include <linux/version.h> #include <media/adp1653.h> @@ -482,24 +481,7 @@ static struct i2c_driver adp1653_i2c_driver = { .id_table = adp1653_id_table, }; -static int __init adp1653_init(void) -{ - int rval; - - rval = i2c_add_driver(&adp1653_i2c_driver); - if (rval) - printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__); - - return rval; -} - -static void __exit adp1653_exit(void) -{ - i2c_del_driver(&adp1653_i2c_driver); -} - -module_init(adp1653_init); -module_exit(adp1653_exit); +module_i2c_driver(adp1653_i2c_driver); MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c index 879f1d8..6bc01fb 100644 --- a/drivers/media/video/adv7170.c +++ b/drivers/media/video/adv7170.c @@ -407,15 +407,4 @@ static struct i2c_driver adv7170_driver = { .id_table = adv7170_id, }; -static __init int init_adv7170(void) -{ - return i2c_add_driver(&adv7170_driver); -} - -static __exit void exit_adv7170(void) -{ - i2c_del_driver(&adv7170_driver); -} - -module_init(init_adv7170); -module_exit(exit_adv7170); +module_i2c_driver(adv7170_driver); diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 206078e..c7640fa 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -457,15 +457,4 @@ static struct i2c_driver adv7175_driver = { .id_table = adv7175_id, }; -static __init int init_adv7175(void) -{ - return i2c_add_driver(&adv7175_driver); -} - -static __exit void exit_adv7175(void) -{ - i2c_del_driver(&adv7175_driver); -} - -module_init(init_adv7175); -module_exit(exit_adv7175); +module_i2c_driver(adv7175_driver); diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index d2138d0..b8b6c4b 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -444,20 +444,8 @@ static struct i2c_driver adv7180_driver = { .id_table = adv7180_id, }; -static __init int adv7180_init(void) -{ - return i2c_add_driver(&adv7180_driver); -} - -static __exit void adv7180_exit(void) -{ - i2c_del_driver(&adv7180_driver); -} - -module_init(adv7180_init); -module_exit(adv7180_exit); +module_i2c_driver(adv7180_driver); MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); MODULE_AUTHOR("Mocean Laboratories"); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c new file mode 100644 index 0000000..e1d4c89 --- /dev/null +++ b/drivers/media/video/adv7183.c @@ -0,0 +1,699 @@ +/* + * adv7183.c Analog Devices ADV7183 video decoder driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/adv7183.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +#include "adv7183_regs.h" + +struct adv7183 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + + v4l2_std_id std; /* Current set standard */ + u32 input; + u32 output; + unsigned reset_pin; + unsigned oe_pin; + struct v4l2_mbus_framefmt fmt; +}; + +/* EXAMPLES USING 27 MHz CLOCK + * Mode 1 CVBS Input (Composite Video on AIN5) + * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. + */ +static const unsigned char adv7183_init_regs[] = { + ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ + ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ + ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ + ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ + ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ + /* ADI recommended programming sequence */ + ADV7183_ADI_CTRL, 0x80, + ADV7183_CTI_DNR_CTRL_4, 0x20, + 0x52, 0x18, + 0x58, 0xED, + 0x77, 0xC5, + 0x7C, 0x93, + 0x7D, 0x00, + 0xD0, 0x48, + 0xD5, 0xA0, + 0xD7, 0xEA, + ADV7183_SD_SATURATION_CR, 0x3E, + ADV7183_PAL_V_END, 0x3E, + ADV7183_PAL_F_TOGGLE, 0x0F, + ADV7183_ADI_CTRL, 0x00, +}; + +static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7183, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7183, hdl)->sd; +} + +static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int adv7183_writeregs(struct v4l2_subdev *sd, + const unsigned char *regs, unsigned int num) +{ + unsigned char reg, data; + unsigned int cnt = 0; + + if (num & 0x1) { + v4l2_err(sd, "invalid regs array\n"); + return -1; + } + + while (cnt < num) { + reg = *regs++; + data = *regs++; + cnt += 2; + + adv7183_write(sd, reg, data); + } + return 0; +} + +static int adv7183_log_status(struct v4l2_subdev *sd) +{ + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_info(sd, "adv7183: Input control = 0x%02x\n", + adv7183_read(sd, ADV7183_IN_CTRL)); + v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", + adv7183_read(sd, ADV7183_VD_SEL)); + v4l2_info(sd, "adv7183: Output control = 0x%02x\n", + adv7183_read(sd, ADV7183_OUT_CTRL)); + v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", + adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); + v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", + adv7183_read(sd, ADV7183_AUTO_DET_EN)); + v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", + adv7183_read(sd, ADV7183_CONTRAST)); + v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", + adv7183_read(sd, ADV7183_BRIGHTNESS)); + v4l2_info(sd, "adv7183: Hue = 0x%02x\n", + adv7183_read(sd, ADV7183_HUE)); + v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_Y)); + v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", + adv7183_read(sd, ADV7183_DEF_C)); + v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL)); + v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", + adv7183_read(sd, ADV7183_POW_MANAGE)); + v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_STATUS_1), + adv7183_read(sd, ADV7183_STATUS_2), + adv7183_read(sd, ADV7183_STATUS_3)); + v4l2_info(sd, "adv7183: Ident = 0x%02x\n", + adv7183_read(sd, ADV7183_IDENT)); + v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", + adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); + v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", + adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); + v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), + adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); + v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", + adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); + v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", + adv7183_read(sd, ADV7183_ADI_CTRL_2)); + v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", + adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); + v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", + adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); + v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", + adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); + v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), + adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); + v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), + adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); + v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_HS_POS_CTRL_1), + adv7183_read(sd, ADV7183_HS_POS_CTRL_2), + adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); + v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", + adv7183_read(sd, ADV7183_POLARITY)); + v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", + adv7183_read(sd, ADV7183_ADC_CTRL)); + v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_OFFSET_CB), + adv7183_read(sd, ADV7183_SD_OFFSET_CR)); + v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", + adv7183_read(sd, ADV7183_SD_SATURATION_CB), + adv7183_read(sd, ADV7183_SD_SATURATION_CR)); + v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", + adv7183_read(sd, ADV7183_DRIVE_STR)); + v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); + return 0; +} + +static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *std = decoder->std; + return 0; +} + +static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + if (std == V4L2_STD_PAL_60) + reg |= 0x60; + else if (std == V4L2_STD_NTSC_443) + reg |= 0x70; + else if (std == V4L2_STD_PAL_N) + reg |= 0x90; + else if (std == V4L2_STD_PAL_M) + reg |= 0xA0; + else if (std == V4L2_STD_PAL_Nc) + reg |= 0xC0; + else if (std & V4L2_STD_PAL) + reg |= 0x80; + else if (std & V4L2_STD_NTSC) + reg |= 0x50; + else if (std & V4L2_STD_SECAM) + reg |= 0xE0; + else + return -EINVAL; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + decoder->std = std; + + return 0; +} + +static int adv7183_reset(struct v4l2_subdev *sd, u32 val) +{ + int reg; + + reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; + adv7183_write(sd, ADV7183_POW_MANAGE, reg); + /* wait 5ms before any further i2c writes are performed */ + usleep_range(5000, 10000); + return 0; +} + +static int adv7183_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) + return -EINVAL; + + if (input != decoder->input) { + decoder->input = input; + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; + switch (input) { + case ADV7183_COMPOSITE1: + reg |= 0x1; + break; + case ADV7183_COMPOSITE2: + reg |= 0x2; + break; + case ADV7183_COMPOSITE3: + reg |= 0x3; + break; + case ADV7183_COMPOSITE4: + reg |= 0x4; + break; + case ADV7183_COMPOSITE5: + reg |= 0x5; + break; + case ADV7183_COMPOSITE6: + reg |= 0xB; + break; + case ADV7183_COMPOSITE7: + reg |= 0xC; + break; + case ADV7183_COMPOSITE8: + reg |= 0xD; + break; + case ADV7183_COMPOSITE9: + reg |= 0xE; + break; + case ADV7183_COMPOSITE10: + reg |= 0xF; + break; + case ADV7183_SVIDEO0: + reg |= 0x6; + break; + case ADV7183_SVIDEO1: + reg |= 0x7; + break; + case ADV7183_SVIDEO2: + reg |= 0x8; + break; + case ADV7183_COMPONENT0: + reg |= 0x9; + break; + case ADV7183_COMPONENT1: + reg |= 0xA; + break; + default: + break; + } + adv7183_write(sd, ADV7183_IN_CTRL, reg); + } + + if (output != decoder->output) { + decoder->output = output; + reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; + switch (output) { + case ADV7183_16BIT_OUT: + reg |= 0x9; + break; + default: + reg |= 0xC; + break; + } + adv7183_write(sd, ADV7183_OUT_CTRL, reg); + } + + return 0; +} + +static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + int val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (val < 0) + val = 127 - val; + adv7183_write(sd, ADV7183_BRIGHTNESS, val); + break; + case V4L2_CID_CONTRAST: + adv7183_write(sd, ADV7183_CONTRAST, val); + break; + case V4L2_CID_SATURATION: + adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); + break; + case V4L2_CID_HUE: + adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); + adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7183 *decoder = to_adv7183(sd); + int reg; + + /* enable autodetection block */ + reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; + adv7183_write(sd, ADV7183_IN_CTRL, reg); + + /* wait autodetection switch */ + mdelay(10); + + /* get autodetection result */ + reg = adv7183_read(sd, ADV7183_STATUS_1); + switch ((reg >> 0x4) & 0x7) { + case 0: + *std = V4L2_STD_NTSC; + break; + case 1: + *std = V4L2_STD_NTSC_443; + break; + case 2: + *std = V4L2_STD_PAL_M; + break; + case 3: + *std = V4L2_STD_PAL_60; + break; + case 4: + *std = V4L2_STD_PAL; + break; + case 5: + *std = V4L2_STD_SECAM; + break; + case 6: + *std = V4L2_STD_PAL_Nc; + break; + case 7: + *std = V4L2_STD_SECAM; + break; + default: + *std = V4L2_STD_UNKNOWN; + break; + } + + /* after std detection, write back user set std */ + adv7183_s_std(sd, decoder->std); + return 0; +} + +static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + int reg; + + *status = V4L2_IN_ST_NO_SIGNAL; + reg = adv7183_read(sd, ADV7183_STATUS_1); + if (reg < 0) + return reg; + if (reg & 0x1) + *status = 0; + return 0; +} + +static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index > 0) + return -EINVAL; + + *code = V4L2_MBUS_FMT_UYVY8_2X8; + return 0; +} + +static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (decoder->std & V4L2_STD_525_60) { + fmt->field = V4L2_FIELD_SEQ_TB; + fmt->width = 720; + fmt->height = 480; + } else { + fmt->field = V4L2_FIELD_SEQ_BT; + fmt->width = 720; + fmt->height = 576; + } + return 0; +} + +static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + adv7183_try_mbus_fmt(sd, fmt); + decoder->fmt = *fmt; + return 0; +} + +static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7183 *decoder = to_adv7183(sd); + + *fmt = decoder->fmt; + return 0; +} + +static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7183 *decoder = to_adv7183(sd); + + if (enable) + gpio_direction_output(decoder->oe_pin, 0); + else + gpio_direction_output(decoder->oe_pin, 1); + udelay(1); + return 0; +} + +static int adv7183_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* 0x11 for adv7183, 0x13 for adv7183b */ + rev = adv7183_read(sd, ADV7183_IDENT); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = adv7183_read(sd, reg->reg & 0xff); + reg->size = 1; + return 0; +} + +static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { + .s_ctrl = adv7183_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7183_core_ops = { + .log_status = adv7183_log_status, + .g_std = adv7183_g_std, + .s_std = adv7183_s_std, + .reset = adv7183_reset, + .g_chip_ident = adv7183_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7183_g_register, + .s_register = adv7183_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops adv7183_video_ops = { + .s_routing = adv7183_s_routing, + .querystd = adv7183_querystd, + .g_input_status = adv7183_g_input_status, + .enum_mbus_fmt = adv7183_enum_mbus_fmt, + .try_mbus_fmt = adv7183_try_mbus_fmt, + .s_mbus_fmt = adv7183_s_mbus_fmt, + .g_mbus_fmt = adv7183_g_mbus_fmt, + .s_stream = adv7183_s_stream, +}; + +static const struct v4l2_subdev_ops adv7183_ops = { + .core = &adv7183_core_ops, + .video = &adv7183_video_ops, +}; + +static int adv7183_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7183 *decoder; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int ret; + struct v4l2_mbus_framefmt fmt; + const unsigned *pin_array; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + pin_array = client->dev.platform_data; + if (pin_array == NULL) + return -EINVAL; + + decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + decoder->reset_pin = pin_array[0]; + decoder->oe_pin = pin_array[1]; + + if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); + ret = -EBUSY; + goto err_free_decoder; + } + + if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { + v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); + ret = -EBUSY; + goto err_free_reset; + } + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7183_ops); + + hdl = &decoder->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); + v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, + V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + ret = hdl->error; + + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + /* v4l2 doesn't support an autodetect standard, pick PAL as default */ + decoder->std = V4L2_STD_PAL; + decoder->input = ADV7183_COMPOSITE4; + decoder->output = ADV7183_8BIT_OUT; + + gpio_direction_output(decoder->oe_pin, 1); + /* reset chip */ + gpio_direction_output(decoder->reset_pin, 0); + /* reset pulse width at least 5ms */ + mdelay(10); + gpio_direction_output(decoder->reset_pin, 1); + /* wait 5ms before any further i2c writes are performed */ + mdelay(5); + + adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); + adv7183_s_std(sd, decoder->std); + fmt.width = 720; + fmt.height = 576; + adv7183_s_mbus_fmt(sd, &fmt); + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + goto err_free_oe; + } + + return 0; +err_free_oe: + gpio_free(decoder->oe_pin); +err_free_reset: + gpio_free(decoder->reset_pin); +err_free_decoder: + kfree(decoder); + return ret; +} + +static int adv7183_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7183 *decoder = to_adv7183(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(decoder->oe_pin); + gpio_free(decoder->reset_pin); + kfree(decoder); + return 0; +} + +static const struct i2c_device_id adv7183_id[] = { + {"adv7183", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7183_id); + +static struct i2c_driver adv7183_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7183", + }, + .probe = adv7183_probe, + .remove = __devexit_p(adv7183_remove), + .id_table = adv7183_id, +}; + +static __init int adv7183_init(void) +{ + return i2c_add_driver(&adv7183_driver); +} + +static __exit void adv7183_exit(void) +{ + i2c_del_driver(&adv7183_driver); +} + +module_init(adv7183_init); +module_exit(adv7183_exit); + +MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h new file mode 100644 index 0000000..4a5b7d2 --- /dev/null +++ b/drivers/media/video/adv7183_regs.h @@ -0,0 +1,107 @@ +/* + * adv7183 - Analog Devices ADV7183 video decoder registers + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ADV7183_REGS_H_ +#define _ADV7183_REGS_H_ + +#define ADV7183_IN_CTRL 0x00 /* Input control */ +#define ADV7183_VD_SEL 0x01 /* Video selection */ +#define ADV7183_OUT_CTRL 0x03 /* Output control */ +#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ +#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ +#define ADV7183_CONTRAST 0x08 /* Contrast */ +#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ +#define ADV7183_HUE 0x0B /* Hue */ +#define ADV7183_DEF_Y 0x0C /* Default value Y */ +#define ADV7183_DEF_C 0x0D /* Default value C */ +#define ADV7183_ADI_CTRL 0x0E /* ADI control */ +#define ADV7183_POW_MANAGE 0x0F /* Power Management */ +#define ADV7183_STATUS_1 0x10 /* Status 1 */ +#define ADV7183_IDENT 0x11 /* Ident */ +#define ADV7183_STATUS_2 0x12 /* Status 2 */ +#define ADV7183_STATUS_3 0x13 /* Status 3 */ +#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ +#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ +#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ +#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ +#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ +#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ +#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ +#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ +#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ +#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ +#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ +#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ +#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ +#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ +#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ +#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ +#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ +#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ +#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ +#define ADV7183_POLARITY 0x37 /* Polarity */ +#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ +#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ +#define ADV7183_ADC_CTRL 0x3A /* ADC control */ +#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ +#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ +#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ +#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ +#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ +#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ +#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ +#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ +#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ +#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ +#define ADV7183_LOCK_CNT 0x51 /* Lock count */ +#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ +#define ADV7183_VBI_INFO 0x90 /* VBI info */ +#define ADV7183_WSS_1 0x91 /* WSS 1 */ +#define ADV7183_WSS_2 0x92 /* WSS 2 */ +#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ +#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ +#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ +#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ +#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ +#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ +#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ +#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ +#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ +#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ +#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ +#define ADV7183_CRC_EN 0xB2 /* CRC enable */ +#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ +#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ +#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ +#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ +#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ +#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ +#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ +#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ +#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ +#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ +#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ +#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ +#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ +#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ +#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ +#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ +#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ + +#endif diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 021fab2..119b604 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -475,15 +475,4 @@ static struct i2c_driver adv7343_driver = { .id_table = adv7343_id, }; -static __init int init_adv7343(void) -{ - return i2c_add_driver(&adv7343_driver); -} - -static __exit void exit_adv7343(void) -{ - i2c_del_driver(&adv7343_driver); -} - -module_init(init_adv7343); -module_exit(exit_adv7343); +module_i2c_driver(adv7343_driver); diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c index 53c496c..ba67465 100644 --- a/drivers/media/video/ak881x.c +++ b/drivers/media/video/ak881x.c @@ -352,18 +352,7 @@ static struct i2c_driver ak881x_i2c_driver = { .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_i2c_driver(ak881x_i2c_driver); MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c new file mode 100644 index 0000000..0bd3813 --- /dev/null +++ b/drivers/media/video/aptina-pll.c @@ -0,0 +1,174 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.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/device.h> +#include <linux/gcd.h> +#include <linux/kernel.h> +#include <linux/lcm.h> +#include <linux/module.h> + +#include "aptina-pll.h" + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll) +{ + unsigned int mf_min; + unsigned int mf_max; + unsigned int p1_min; + unsigned int p1_max; + unsigned int p1; + unsigned int div; + + dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", + pll->ext_clock, pll->pix_clock); + + if (pll->ext_clock < limits->ext_clock_min || + pll->ext_clock > limits->ext_clock_max) { + dev_err(dev, "pll: invalid external clock frequency.\n"); + return -EINVAL; + } + + if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { + dev_err(dev, "pll: invalid pixel clock frequency.\n"); + return -EINVAL; + } + + /* Compute the multiplier M and combined N*P1 divisor. */ + div = gcd(pll->pix_clock, pll->ext_clock); + pll->m = pll->pix_clock / div; + div = pll->ext_clock / div; + + /* We now have the smallest M and N*P1 values that will result in the + * desired pixel clock frequency, but they might be out of the valid + * range. Compute the factor by which we should multiply them given the + * following constraints: + * + * - minimum/maximum multiplier + * - minimum/maximum multiplier output clock frequency assuming the + * minimum/maximum N value + * - minimum/maximum combined N*P1 divisor + */ + mf_min = DIV_ROUND_UP(limits->m_min, pll->m); + mf_min = max(mf_min, limits->out_clock_min / + (pll->ext_clock / limits->n_min * pll->m)); + mf_min = max(mf_min, limits->n_min * limits->p1_min / div); + mf_max = limits->m_max / pll->m; + mf_max = min(mf_max, limits->out_clock_max / + (pll->ext_clock / limits->n_max * pll->m)); + mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); + + dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); + if (mf_min > mf_max) { + dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); + return -EINVAL; + } + + /* + * We're looking for the highest acceptable P1 value for which a + * multiplier factor MF exists that fulfills the following conditions: + * + * 1. p1 is in the [p1_min, p1_max] range given by the limits and is + * even + * 2. mf is in the [mf_min, mf_max] range computed above + * 3. div * mf is a multiple of p1, in order to compute + * n = div * mf / p1 + * m = pll->m * mf + * 4. the internal clock frequency, given by ext_clock / n, is in the + * [int_clock_min, int_clock_max] range given by the limits + * 5. the output clock frequency, given by ext_clock / n * m, is in the + * [out_clock_min, out_clock_max] range given by the limits + * + * The first naive approach is to iterate over all p1 values acceptable + * according to (1) and all mf values acceptable according to (2), and + * stop at the first combination that fulfills (3), (4) and (5). This + * has a O(n^2) complexity. + * + * Instead of iterating over all mf values in the [mf_min, mf_max] range + * we can compute the mf increment between two acceptable values + * according to (3) with + * + * mf_inc = p1 / gcd(div, p1) (6) + * + * and round the minimum up to the nearest multiple of mf_inc. This will + * restrict the number of mf values to be checked. + * + * Furthermore, conditions (4) and (5) only restrict the range of + * acceptable p1 and mf values by modifying the minimum and maximum + * limits. (5) can be expressed as + * + * ext_clock / (div * mf / p1) * m * mf >= out_clock_min + * ext_clock / (div * mf / p1) * m * mf <= out_clock_max + * + * or + * + * p1 >= out_clock_min * div / (ext_clock * m) (7) + * p1 <= out_clock_max * div / (ext_clock * m) + * + * Similarly, (4) can be expressed as + * + * mf >= ext_clock * p1 / (int_clock_max * div) (8) + * mf <= ext_clock * p1 / (int_clock_min * div) + * + * We can thus iterate over the restricted p1 range defined by the + * combination of (1) and (7), and then compute the restricted mf range + * defined by the combination of (2), (6) and (8). If the resulting mf + * range is not empty, any value in the mf range is acceptable. We thus + * select the mf lwoer bound and the corresponding p1 value. + */ + if (limits->p1_min == 0) { + dev_err(dev, "pll: P1 minimum value must be >0.\n"); + return -EINVAL; + } + + p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, + pll->ext_clock * pll->m)); + p1_max = min(limits->p1_max, limits->out_clock_max * div / + (pll->ext_clock * pll->m)); + + for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { + unsigned int mf_inc = p1 / gcd(div, p1); + unsigned int mf_high; + unsigned int mf_low; + + mf_low = max(roundup(mf_min, mf_inc), + DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)); + mf_high = min(mf_max, pll->ext_clock * p1 / + (limits->int_clock_min * div)); + + if (mf_low > mf_high) + continue; + + pll->n = div * mf_low / p1; + pll->m *= mf_low; + pll->p1 = p1; + dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); + return 0; + } + + dev_err(dev, "pll: no valid N and P1 divisors found.\n"); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(aptina_pll_calculate); + +MODULE_DESCRIPTION("Aptina PLL Helpers"); +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h new file mode 100644 index 0000000..b370e34 --- /dev/null +++ b/drivers/media/video/aptina-pll.h @@ -0,0 +1,56 @@ +/* + * Aptina Sensor PLL Configuration + * + * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.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 + */ + +#ifndef __APTINA_PLL_H +#define __APTINA_PLL_H + +struct aptina_pll { + unsigned int ext_clock; + unsigned int pix_clock; + + unsigned int n; + unsigned int m; + unsigned int p1; +}; + +struct aptina_pll_limits { + unsigned int ext_clock_min; + unsigned int ext_clock_max; + unsigned int int_clock_min; + unsigned int int_clock_max; + unsigned int out_clock_min; + unsigned int out_clock_max; + unsigned int pix_clock_max; + + unsigned int n_min; + unsigned int n_max; + unsigned int m_min; + unsigned int m_max; + unsigned int p1_min; + unsigned int p1_max; +}; + +struct device; + +int aptina_pll_calculate(struct device *dev, + const struct aptina_pll_limits *limits, + struct aptina_pll *pll); + +#endif /* __APTINA_PLL_H */ diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c index f241702..7a3371f 100644 --- a/drivers/media/video/as3645a.c +++ b/drivers/media/video/as3645a.c @@ -881,24 +881,7 @@ static struct i2c_driver as3645a_i2c_driver = { .id_table = as3645a_id_table, }; -static int __init as3645a_init(void) -{ - int rval; - - rval = i2c_add_driver(&as3645a_i2c_driver); - if (rval) - pr_err("%s: Failed to register the driver\n", AS3645A_NAME); - - return rval; -} - -static void __exit as3645a_exit(void) -{ - i2c_del_driver(&as3645a_i2c_driver); -} - -module_init(as3645a_init); -module_exit(as3645a_exit); +module_i2c_driver(as3645a_i2c_driver); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig new file mode 100644 index 0000000..ecd5323 --- /dev/null +++ b/drivers/media/video/blackfin/Kconfig @@ -0,0 +1,10 @@ +config VIDEO_BLACKFIN_CAPTURE + tristate "Blackfin Video Capture Driver" + depends on VIDEO_V4L2 && BLACKFIN && I2C + select VIDEOBUF2_DMA_CONTIG + help + V4L2 bridge driver for Blackfin video capture device. + Choose PPI or EPPI as its interface. + + To compile this driver as a module, choose M here: the + module will be called bfin_video_capture. diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile new file mode 100644 index 0000000..aa3a0a2 --- /dev/null +++ b/drivers/media/video/blackfin/Makefile @@ -0,0 +1,2 @@ +bfin_video_capture-objs := bfin_capture.o ppi.o +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c new file mode 100644 index 0000000..514fcf7 --- /dev/null +++ b/drivers/media/video/blackfin/bfin_capture.c @@ -0,0 +1,1059 @@ +/* + * Analog Devices video capture driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/completion.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/io.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#include <asm/dma.h> + +#include <media/blackfin/bfin_capture.h> +#include <media/blackfin/ppi.h> + +#define CAPTURE_DRV_NAME "bfin_capture" +#define BCAP_MIN_NUM_BUF 2 + +struct bcap_format { + char *desc; + u32 pixelformat; + enum v4l2_mbus_pixelcode mbus_code; + int bpp; /* bits per pixel */ +}; + +struct bcap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +struct bcap_device { + /* capture device instance */ + struct v4l2_device v4l2_dev; + /* v4l2 control handler */ + struct v4l2_ctrl_handler ctrl_handler; + /* device node data */ + struct video_device *video_dev; + /* sub device instance */ + struct v4l2_subdev *sd; + /* capture config */ + struct bfin_capture_config *cfg; + /* ppi interface */ + struct ppi_if *ppi; + /* current input */ + unsigned int cur_input; + /* current selected standard */ + v4l2_std_id std; + /* used to store pixel format */ + struct v4l2_pix_format fmt; + /* bits per pixel*/ + int bpp; + /* used to store sensor supported format */ + struct bcap_format *sensor_formats; + /* number of sensor formats array */ + int num_sensor_formats; + /* pointing to current video buffer */ + struct bcap_buffer *cur_frm; + /* pointing to next video buffer */ + struct bcap_buffer *next_frm; + /* buffer queue used in videobuf2 */ + struct vb2_queue buffer_queue; + /* allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* queue of filled frames */ + struct list_head dma_queue; + /* used in videobuf2 callback */ + spinlock_t lock; + /* used to access capture device */ + struct mutex mutex; + /* used to wait ppi to complete one transfer */ + struct completion comp; + /* prepare to stop */ + bool stop; +}; + +struct bcap_fh { + struct v4l2_fh fh; + /* indicates whether this file handle is doing IO */ + bool io_allowed; +}; + +static const struct bcap_format bcap_formats[] = { + { + .desc = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .bpp = 16, + }, + { + .desc = "YCbCr 4:2:2 Interleaved YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 16, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, + .bpp = 16, + }, + +}; +#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats) + +static irqreturn_t bcap_isr(int irq, void *dev_id); + +static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb) +{ + return container_of(vb, struct bcap_buffer, vb); +} + +static int bcap_init_sensor_formats(struct bcap_device *bcap_dev) +{ + enum v4l2_mbus_pixelcode code; + struct bcap_format *sf; + unsigned int num_formats = 0; + int i, j; + + while (!v4l2_subdev_call(bcap_dev->sd, video, + enum_mbus_fmt, num_formats, &code)) + num_formats++; + if (!num_formats) + return -ENXIO; + + sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL); + if (!sf) + return -ENOMEM; + + for (i = 0; i < num_formats; i++) { + v4l2_subdev_call(bcap_dev->sd, video, + enum_mbus_fmt, i, &code); + for (j = 0; j < BCAP_MAX_FMTS; j++) + if (code == bcap_formats[j].mbus_code) + break; + if (j == BCAP_MAX_FMTS) { + /* we don't allow this sensor working with our bridge */ + kfree(sf); + return -EINVAL; + } + sf[i] = bcap_formats[j]; + } + bcap_dev->sensor_formats = sf; + bcap_dev->num_sensor_formats = num_formats; + return 0; +} + +static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) +{ + bcap_dev->num_sensor_formats = 0; + kfree(bcap_dev->sensor_formats); + bcap_dev->sensor_formats = NULL; +} + +static int bcap_open(struct file *file) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct video_device *vfd = bcap_dev->video_dev; + struct bcap_fh *bcap_fh; + + if (!bcap_dev->sd) { + v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n"); + return -ENODEV; + } + + bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL); + if (!bcap_fh) { + v4l2_err(&bcap_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + + v4l2_fh_init(&bcap_fh->fh, vfd); + + /* store pointer to v4l2_fh in private_data member of file */ + file->private_data = &bcap_fh->fh; + v4l2_fh_add(&bcap_fh->fh); + bcap_fh->io_allowed = false; + return 0; +} + +static int bcap_release(struct file *file) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + /* if this instance is doing IO */ + if (bcap_fh->io_allowed) + vb2_queue_release(&bcap_dev->buffer_queue); + + file->private_data = NULL; + v4l2_fh_del(&bcap_fh->fh); + v4l2_fh_exit(&bcap_fh->fh); + kfree(bcap_fh); + return 0; +} + +static int bcap_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_mmap(&bcap_dev->buffer_queue, vma); +} + +#ifndef CONFIG_MMU +static unsigned long bcap_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_get_unmapped_area(&bcap_dev->buffer_queue, + addr, + len, + pgoff, + flags); +} +#endif + +static unsigned int bcap_poll(struct file *file, poll_table *wait) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_poll(&bcap_dev->buffer_queue, file, wait); +} + +static int bcap_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + + if (*nbuffers < BCAP_MIN_NUM_BUF) + *nbuffers = BCAP_MIN_NUM_BUF; + + *nplanes = 1; + sizes[0] = bcap_dev->fmt.sizeimage; + alloc_ctxs[0] = bcap_dev->alloc_ctx; + + return 0; +} + +static int bcap_buffer_init(struct vb2_buffer *vb) +{ + struct bcap_buffer *buf = to_bcap_vb(vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +static int bcap_buffer_prepare(struct vb2_buffer *vb) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct bcap_buffer *buf = to_bcap_vb(vb); + unsigned long size; + + size = bcap_dev->fmt.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buf->vb, 0, size); + + return 0; +} + +static void bcap_buffer_queue(struct vb2_buffer *vb) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct bcap_buffer *buf = to_bcap_vb(vb); + unsigned long flags; + + spin_lock_irqsave(&bcap_dev->lock, flags); + list_add_tail(&buf->list, &bcap_dev->dma_queue); + spin_unlock_irqrestore(&bcap_dev->lock, flags); +} + +static void bcap_buffer_cleanup(struct vb2_buffer *vb) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct bcap_buffer *buf = to_bcap_vb(vb); + unsigned long flags; + + spin_lock_irqsave(&bcap_dev->lock, flags); + list_del_init(&buf->list); + spin_unlock_irqrestore(&bcap_dev->lock, flags); +} + +static void bcap_lock(struct vb2_queue *vq) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + mutex_lock(&bcap_dev->mutex); +} + +static void bcap_unlock(struct vb2_queue *vq) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + mutex_unlock(&bcap_dev->mutex); +} + +static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + struct ppi_if *ppi = bcap_dev->ppi; + struct ppi_params params; + int ret; + + /* enable streamon on the sub device */ + ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1); + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n"); + return ret; + } + + /* set ppi params */ + params.width = bcap_dev->fmt.width; + params.height = bcap_dev->fmt.height; + params.bpp = bcap_dev->bpp; + params.ppi_control = bcap_dev->cfg->ppi_control; + params.int_mask = bcap_dev->cfg->int_mask; + params.blank_clocks = bcap_dev->cfg->blank_clocks; + ret = ppi->ops->set_params(ppi, ¶ms); + if (ret < 0) { + v4l2_err(&bcap_dev->v4l2_dev, + "Error in setting ppi params\n"); + return ret; + } + + /* attach ppi DMA irq handler */ + ret = ppi->ops->attach_irq(ppi, bcap_isr); + if (ret < 0) { + v4l2_err(&bcap_dev->v4l2_dev, + "Error in attaching interrupt handler\n"); + return ret; + } + + INIT_COMPLETION(bcap_dev->comp); + bcap_dev->stop = false; + return 0; +} + +static int bcap_stop_streaming(struct vb2_queue *vq) +{ + struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); + struct ppi_if *ppi = bcap_dev->ppi; + int ret; + + if (!vb2_is_streaming(vq)) + return 0; + + bcap_dev->stop = true; + wait_for_completion(&bcap_dev->comp); + ppi->ops->stop(ppi); + ppi->ops->detach_irq(ppi); + ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&bcap_dev->v4l2_dev, + "stream off failed in subdev\n"); + + /* release all active buffers */ + while (!list_empty(&bcap_dev->dma_queue)) { + bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + list_del(&bcap_dev->next_frm->list); + vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR); + } + return 0; +} + +static struct vb2_ops bcap_video_qops = { + .queue_setup = bcap_queue_setup, + .buf_init = bcap_buffer_init, + .buf_prepare = bcap_buffer_prepare, + .buf_cleanup = bcap_buffer_cleanup, + .buf_queue = bcap_buffer_queue, + .wait_prepare = bcap_unlock, + .wait_finish = bcap_lock, + .start_streaming = bcap_start_streaming, + .stop_streaming = bcap_stop_streaming, +}; + +static int bcap_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct vb2_queue *vq = &bcap_dev->buffer_queue; + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + if (vb2_is_busy(vq)) + return -EBUSY; + + bcap_fh->io_allowed = true; + + return vb2_reqbufs(vq, req_buf); +} + +static int bcap_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return vb2_querybuf(&bcap_dev->buffer_queue, buf); +} + +static int bcap_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + if (!bcap_fh->io_allowed) + return -EBUSY; + + return vb2_qbuf(&bcap_dev->buffer_queue, buf); +} + +static int bcap_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_fh *fh = file->private_data; + struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); + + if (!bcap_fh->io_allowed) + return -EBUSY; + + return vb2_dqbuf(&bcap_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +static irqreturn_t bcap_isr(int irq, void *dev_id) +{ + struct ppi_if *ppi = dev_id; + struct bcap_device *bcap_dev = ppi->priv; + struct timeval timevalue; + struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; + dma_addr_t addr; + + spin_lock(&bcap_dev->lock); + + if (bcap_dev->cur_frm != bcap_dev->next_frm) { + do_gettimeofday(&timevalue); + vb->v4l2_buf.timestamp = timevalue; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + bcap_dev->cur_frm = bcap_dev->next_frm; + } + + ppi->ops->stop(ppi); + + if (bcap_dev->stop) { + complete(&bcap_dev->comp); + } else { + if (!list_empty(&bcap_dev->dma_queue)) { + bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + list_del(&bcap_dev->next_frm->list); + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0); + ppi->ops->update_addr(ppi, (unsigned long)addr); + } + ppi->ops->start(ppi); + } + + spin_unlock(&bcap_dev->lock); + + return IRQ_HANDLED; +} + +static int bcap_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bcap_fh *fh = file->private_data; + struct ppi_if *ppi = bcap_dev->ppi; + dma_addr_t addr; + int ret; + + if (!fh->io_allowed) + return -EBUSY; + + /* call streamon to start streaming in videobuf */ + ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type); + if (ret) + return ret; + + /* if dma queue is empty, return error */ + if (list_empty(&bcap_dev->dma_queue)) { + v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n"); + ret = -EINVAL; + goto err; + } + + /* get the next frame from the dma queue */ + bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + bcap_dev->cur_frm = bcap_dev->next_frm; + /* remove buffer from the dma queue */ + list_del(&bcap_dev->cur_frm->list); + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + /* update DMA address */ + ppi->ops->update_addr(ppi, (unsigned long)addr); + /* enable ppi */ + ppi->ops->start(ppi); + + return 0; +err: + vb2_streamoff(&bcap_dev->buffer_queue, buf_type); + return ret; +} + +static int bcap_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bcap_fh *fh = file->private_data; + + if (!fh->io_allowed) + return -EBUSY; + + return vb2_streamoff(&bcap_dev->buffer_queue, buf_type); +} + +static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, video, querystd, std); +} + +static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + *std = bcap_dev->std; + return 0; +} + +static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std); + if (ret < 0) + return ret; + + bcap_dev->std = *std; + return 0; +} + +static int bcap_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bfin_capture_config *config = bcap_dev->cfg; + int ret; + u32 status; + + if (input->index >= config->num_inputs) + return -EINVAL; + + *input = config->inputs[input->index]; + /* get input status */ + ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status); + if (!ret) + input->status = status; + return 0; +} + +static int bcap_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + *index = bcap_dev->cur_input; + return 0; +} + +static int bcap_s_input(struct file *file, void *priv, unsigned int index) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bfin_capture_config *config = bcap_dev->cfg; + struct bcap_route *route; + int ret; + + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + if (index >= config->num_inputs) + return -EINVAL; + + route = &config->routes[index]; + ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, + route->input, route->output, 0); + if ((ret < 0) && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); + return ret; + } + bcap_dev->cur_input = index; + return 0; +} + +static int bcap_try_format(struct bcap_device *bcap, + struct v4l2_pix_format *pixfmt, + enum v4l2_mbus_pixelcode *mbus_code, + int *bpp) +{ + struct bcap_format *sf = bcap->sensor_formats; + struct bcap_format *fmt = NULL; + struct v4l2_mbus_framefmt mbus_fmt; + int ret, i; + + for (i = 0; i < bcap->num_sensor_formats; i++) { + fmt = &sf[i]; + if (pixfmt->pixelformat == fmt->pixelformat) + break; + } + if (i == bcap->num_sensor_formats) + fmt = &sf[0]; + + if (mbus_code) + *mbus_code = fmt->mbus_code; + if (bpp) + *bpp = fmt->bpp; + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); + ret = v4l2_subdev_call(bcap->sd, video, + try_mbus_fmt, &mbus_fmt); + if (ret < 0) + return ret; + v4l2_fill_pix_format(pixfmt, &mbus_fmt); + pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + return 0; +} + +static int bcap_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct bcap_format *sf = bcap_dev->sensor_formats; + + if (fmt->index >= bcap_dev->num_sensor_formats) + return -EINVAL; + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strlcpy(fmt->description, + sf[fmt->index].desc, + sizeof(fmt->description)); + fmt->pixelformat = sf[fmt->index].pixelformat; + return 0; +} + +static int bcap_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + return bcap_try_format(bcap_dev, pixfmt, NULL, NULL); +} + +static int bcap_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + fmt->fmt.pix = bcap_dev->fmt; + return 0; +} + +static int bcap_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + struct v4l2_mbus_framefmt mbus_fmt; + enum v4l2_mbus_pixelcode mbus_code; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + int ret, bpp; + + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + /* see if format works */ + ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp); + if (ret < 0) + return ret; + + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code); + ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); + if (ret < 0) + return ret; + bcap_dev->fmt = *pixfmt; + bcap_dev->bpp = bpp; + return 0; +} + +static int bcap_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int bcap_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a); +} + +static int bcap_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a); +} + +static int bcap_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + return v4l2_subdev_call(bcap_dev->sd, core, + g_chip_ident, chip); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int bcap_dbg_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, core, + g_register, reg); +} + +static int bcap_dbg_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, core, + s_register, reg); +} +#endif + +static int bcap_log_status(struct file *file, void *priv) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + /* status for sub devices */ + v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status); + return 0; +} + +static const struct v4l2_ioctl_ops bcap_ioctl_ops = { + .vidioc_querycap = bcap_querycap, + .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap, + .vidioc_enum_input = bcap_enum_input, + .vidioc_g_input = bcap_g_input, + .vidioc_s_input = bcap_s_input, + .vidioc_querystd = bcap_querystd, + .vidioc_s_std = bcap_s_std, + .vidioc_g_std = bcap_g_std, + .vidioc_reqbufs = bcap_reqbufs, + .vidioc_querybuf = bcap_querybuf, + .vidioc_qbuf = bcap_qbuf, + .vidioc_dqbuf = bcap_dqbuf, + .vidioc_streamon = bcap_streamon, + .vidioc_streamoff = bcap_streamoff, + .vidioc_g_parm = bcap_g_parm, + .vidioc_s_parm = bcap_s_parm, + .vidioc_g_chip_ident = bcap_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = bcap_dbg_g_register, + .vidioc_s_register = bcap_dbg_s_register, +#endif + .vidioc_log_status = bcap_log_status, +}; + +static struct v4l2_file_operations bcap_fops = { + .owner = THIS_MODULE, + .open = bcap_open, + .release = bcap_release, + .unlocked_ioctl = video_ioctl2, + .mmap = bcap_mmap, +#ifndef CONFIG_MMU + .get_unmapped_area = bcap_get_unmapped_area, +#endif + .poll = bcap_poll +}; + +static int __devinit bcap_probe(struct platform_device *pdev) +{ + struct bcap_device *bcap_dev; + struct video_device *vfd; + struct i2c_adapter *i2c_adap; + struct bfin_capture_config *config; + struct vb2_queue *q; + int ret; + + config = pdev->dev.platform_data; + if (!config) { + v4l2_err(pdev->dev.driver, "Unable to get board config\n"); + return -ENODEV; + } + + bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL); + if (!bcap_dev) { + v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n"); + return -ENOMEM; + } + + bcap_dev->cfg = config; + + bcap_dev->ppi = ppi_create_instance(config->ppi_info); + if (!bcap_dev->ppi) { + v4l2_err(pdev->dev.driver, "Unable to create ppi\n"); + ret = -ENODEV; + goto err_free_dev; + } + bcap_dev->ppi->priv = bcap_dev; + + bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(bcap_dev->alloc_ctx)) { + ret = PTR_ERR(bcap_dev->alloc_ctx); + goto err_free_ppi; + } + + vfd = video_device_alloc(); + if (!vfd) { + ret = -ENOMEM; + v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); + goto err_cleanup_ctx; + } + + /* initialize field of video device */ + vfd->release = video_device_release; + vfd->fops = &bcap_fops; + vfd->ioctl_ops = &bcap_ioctl_ops; + vfd->tvnorms = 0; + vfd->v4l2_dev = &bcap_dev->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name)); + bcap_dev->video_dev = vfd; + + ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device\n"); + goto err_release_vdev; + } + v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); + + bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler; + ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to init control handler\n"); + goto err_unreg_v4l2; + } + + spin_lock_init(&bcap_dev->lock); + /* initialize queue */ + q = &bcap_dev->buffer_queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP; + q->drv_priv = bcap_dev; + q->buf_struct_size = sizeof(struct bcap_buffer); + q->ops = &bcap_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + + vb2_queue_init(q); + + mutex_init(&bcap_dev->mutex); + init_completion(&bcap_dev->comp); + + /* init video dma queues */ + INIT_LIST_HEAD(&bcap_dev->dma_queue); + + vfd->lock = &bcap_dev->mutex; + + /* register video device */ + ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to register video device\n"); + goto err_free_handler; + } + video_set_drvdata(bcap_dev->video_dev, bcap_dev); + v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n", + video_device_node_name(vfd)); + + /* load up the subdevice */ + i2c_adap = i2c_get_adapter(config->i2c_adapter_id); + if (!i2c_adap) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to find i2c adapter\n"); + goto err_unreg_vdev; + + } + bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev, + i2c_adap, + &config->board_info, + NULL); + if (bcap_dev->sd) { + int i; + /* update tvnorms from the sub devices */ + for (i = 0; i < config->num_inputs; i++) + vfd->tvnorms |= config->inputs[i].std; + } else { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to register sub device\n"); + goto err_unreg_vdev; + } + + v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); + + /* now we can probe the default state */ + if (vfd->tvnorms) { + v4l2_std_id std; + ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to get std\n"); + goto err_unreg_vdev; + } + bcap_dev->std = std; + } + ret = bcap_init_sensor_formats(bcap_dev); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to create sensor formats table\n"); + goto err_unreg_vdev; + } + return 0; +err_unreg_vdev: + video_unregister_device(bcap_dev->video_dev); + bcap_dev->video_dev = NULL; +err_free_handler: + v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); +err_unreg_v4l2: + v4l2_device_unregister(&bcap_dev->v4l2_dev); +err_release_vdev: + if (bcap_dev->video_dev) + video_device_release(bcap_dev->video_dev); +err_cleanup_ctx: + vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); +err_free_ppi: + ppi_delete_instance(bcap_dev->ppi); +err_free_dev: + kfree(bcap_dev); + return ret; +} + +static int __devexit bcap_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct bcap_device *bcap_dev = container_of(v4l2_dev, + struct bcap_device, v4l2_dev); + + bcap_free_sensor_formats(bcap_dev); + video_unregister_device(bcap_dev->video_dev); + v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); + v4l2_device_unregister(v4l2_dev); + vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); + ppi_delete_instance(bcap_dev->ppi); + kfree(bcap_dev); + return 0; +} + +static struct platform_driver bcap_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = bcap_probe, + .remove = __devexit_p(bcap_remove), +}; + +static __init int bcap_init(void) +{ + return platform_driver_register(&bcap_driver); +} + +static __exit void bcap_exit(void) +{ + platform_driver_unregister(&bcap_driver); +} + +module_init(bcap_init); +module_exit(bcap_exit); + +MODULE_DESCRIPTION("Analog Devices blackfin video capture driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c new file mode 100644 index 0000000..d295921 --- /dev/null +++ b/drivers/media/video/blackfin/ppi.c @@ -0,0 +1,271 @@ +/* + * ppi.c Analog Devices Parallel Peripheral Interface driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/slab.h> + +#include <asm/bfin_ppi.h> +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include <media/blackfin/ppi.h> + +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler); +static void ppi_detach_irq(struct ppi_if *ppi); +static int ppi_start(struct ppi_if *ppi); +static int ppi_stop(struct ppi_if *ppi); +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params); +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr); + +static const struct ppi_ops ppi_ops = { + .attach_irq = ppi_attach_irq, + .detach_irq = ppi_detach_irq, + .start = ppi_start, + .stop = ppi_stop, + .set_params = ppi_set_params, + .update_addr = ppi_update_addr, +}; + +static irqreturn_t ppi_irq_err(int irq, void *dev_id) +{ + struct ppi_if *ppi = dev_id; + const struct ppi_info *info = ppi->info; + + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + unsigned short status; + + /* register on bf561 is cleared when read + * others are W1C + */ + status = bfin_read16(®->status); + bfin_write16(®->status, 0xff00); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + bfin_write16(®->status, 0xffff); + break; + } + default: + break; + } + + return IRQ_HANDLED; +} + +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler) +{ + const struct ppi_info *info = ppi->info; + int ret; + + ret = request_dma(info->dma_ch, "PPI_DMA"); + + if (ret) { + pr_err("Unable to allocate DMA channel for PPI\n"); + return ret; + } + set_dma_callback(info->dma_ch, handler, ppi); + + if (ppi->err_int) { + ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi); + if (ret) { + pr_err("Unable to allocate IRQ for PPI\n"); + free_dma(info->dma_ch); + } + } + return ret; +} + +static void ppi_detach_irq(struct ppi_if *ppi) +{ + const struct ppi_info *info = ppi->info; + + if (ppi->err_int) + free_irq(info->irq_err, ppi); + free_dma(info->dma_ch); +} + +static int ppi_start(struct ppi_if *ppi) +{ + const struct ppi_info *info = ppi->info; + + /* enable DMA */ + enable_dma(info->dma_ch); + + /* enable PPI */ + ppi->ppi_control |= PORT_EN; + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + bfin_write16(®->control, ppi->ppi_control); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + bfin_write32(®->control, ppi->ppi_control); + break; + } + default: + return -EINVAL; + } + + SSYNC(); + return 0; +} + +static int ppi_stop(struct ppi_if *ppi) +{ + const struct ppi_info *info = ppi->info; + + /* disable PPI */ + ppi->ppi_control &= ~PORT_EN; + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + bfin_write16(®->control, ppi->ppi_control); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + bfin_write32(®->control, ppi->ppi_control); + break; + } + default: + return -EINVAL; + } + + /* disable DMA */ + clear_dma_irqstat(info->dma_ch); + disable_dma(info->dma_ch); + + SSYNC(); + return 0; +} + +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) +{ + const struct ppi_info *info = ppi->info; + int dma32 = 0; + int dma_config, bytes_per_line, lines_per_frame; + + bytes_per_line = params->width * params->bpp / 8; + lines_per_frame = params->height; + if (params->int_mask == 0xFFFFFFFF) + ppi->err_int = false; + else + ppi->err_int = true; + + dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN); + ppi->ppi_control = params->ppi_control & ~PORT_EN; + switch (info->type) { + case PPI_TYPE_PPI: + { + struct bfin_ppi_regs *reg = info->base; + + if (params->ppi_control & DMA32) + dma32 = 1; + + bfin_write16(®->control, ppi->ppi_control); + bfin_write16(®->count, bytes_per_line - 1); + bfin_write16(®->frame, lines_per_frame); + break; + } + case PPI_TYPE_EPPI: + { + struct bfin_eppi_regs *reg = info->base; + + if ((params->ppi_control & PACK_EN) + || (params->ppi_control & 0x38000) > DLEN_16) + dma32 = 1; + + bfin_write32(®->control, ppi->ppi_control); + bfin_write16(®->line, bytes_per_line + params->blank_clocks); + bfin_write16(®->frame, lines_per_frame); + bfin_write16(®->hdelay, 0); + bfin_write16(®->vdelay, 0); + bfin_write16(®->hcount, bytes_per_line); + bfin_write16(®->vcount, lines_per_frame); + break; + } + default: + return -EINVAL; + } + + if (dma32) { + dma_config |= WDSIZE_32; + set_dma_x_count(info->dma_ch, bytes_per_line >> 2); + set_dma_x_modify(info->dma_ch, 4); + set_dma_y_modify(info->dma_ch, 4); + } else { + dma_config |= WDSIZE_16; + set_dma_x_count(info->dma_ch, bytes_per_line >> 1); + set_dma_x_modify(info->dma_ch, 2); + set_dma_y_modify(info->dma_ch, 2); + } + set_dma_y_count(info->dma_ch, lines_per_frame); + set_dma_config(info->dma_ch, dma_config); + + SSYNC(); + return 0; +} + +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr) +{ + set_dma_start_addr(ppi->info->dma_ch, addr); +} + +struct ppi_if *ppi_create_instance(const struct ppi_info *info) +{ + struct ppi_if *ppi; + + if (!info || !info->pin_req) + return NULL; + + if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { + pr_err("request peripheral failed\n"); + return NULL; + } + + ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); + if (!ppi) { + peripheral_free_list(info->pin_req); + pr_err("unable to allocate memory for ppi handle\n"); + return NULL; + } + ppi->ops = &ppi_ops; + ppi->info = info; + + pr_info("ppi probe success\n"); + return ppi; +} + +void ppi_delete_instance(struct ppi_if *ppi) +{ + peripheral_free_list(ppi->info->pin_req); + kfree(ppi); +} diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 859eabf..377bf05 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -514,15 +514,4 @@ static struct i2c_driver bt819_driver = { .id_table = bt819_id, }; -static __init int init_bt819(void) -{ - return i2c_add_driver(&bt819_driver); -} - -static __exit void exit_bt819(void) -{ - i2c_del_driver(&bt819_driver); -} - -module_init(init_bt819); -module_exit(exit_bt819); +module_i2c_driver(bt819_driver); diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c index a43059d..7e5bd36 100644 --- a/drivers/media/video/bt856.c +++ b/drivers/media/video/bt856.c @@ -270,15 +270,4 @@ static struct i2c_driver bt856_driver = { .id_table = bt856_id, }; -static __init int init_bt856(void) -{ - return i2c_add_driver(&bt856_driver); -} - -static __exit void exit_bt856(void) -{ - i2c_del_driver(&bt856_driver); -} - -module_init(init_bt856); -module_exit(exit_bt856); +module_i2c_driver(bt856_driver); diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c index 4e5dcea..905320b 100644 --- a/drivers/media/video/bt866.c +++ b/drivers/media/video/bt866.c @@ -240,15 +240,4 @@ static struct i2c_driver bt866_driver = { .id_table = bt866_id, }; -static __init int init_bt866(void) -{ - return i2c_add_driver(&bt866_driver); -} - -static __exit void exit_bt866(void) -{ - i2c_del_driver(&bt866_driver); -} - -module_init(init_bt866); -module_exit(exit_bt866); +module_i2c_driver(bt866_driver); diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 76c301f..e581b37 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2035,11 +2035,7 @@ static int bttv_log_status(struct file *file, void *f) struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - pr_info("%d: ======== START STATUS CARD #%d ========\n", - btv->c.nr, btv->c.nr); bttv_call_all(btv, core, log_status); - pr_info("%d: ======== END STATUS CARD #%d ========\n", - btv->c.nr, btv->c.nr); return 0; } diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 1d64af9..c8581e2 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -249,15 +249,4 @@ static struct i2c_driver cs5345_driver = { .id_table = cs5345_id, }; -static __init int init_cs5345(void) -{ - return i2c_add_driver(&cs5345_driver); -} - -static __exit void exit_cs5345(void) -{ - i2c_del_driver(&cs5345_driver); -} - -module_init(init_cs5345); -module_exit(exit_cs5345); +module_i2c_driver(cs5345_driver); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 51c5b9a..b293912 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -248,15 +248,4 @@ static struct i2c_driver cs53l32a_driver = { .id_table = cs53l32a_id, }; -static __init int init_cs53l32a(void) -{ - return i2c_add_driver(&cs53l32a_driver); -} - -static __exit void exit_cs53l32a(void) -{ - i2c_del_driver(&cs53l32a_driver); -} - -module_init(init_cs53l32a); -module_exit(exit_cs53l32a); +module_i2c_driver(cs53l32a_driver); diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index a1e6c2a..e118361 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -285,7 +285,6 @@ static void __exit cx18_alsa_exit(void) drv = driver_find("cx18", &pci_bus_type); ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); - put_driver(drv); cx18_ext_init = NULL; printk(KERN_INFO "cx18-alsa: module unload complete\n"); diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 349bd9c..b55d57c 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -38,7 +38,7 @@ #include "cx18-ioctl.h" #include "cx18-controls.h" #include "tuner-xc2028.h" - +#include <linux/dma-mapping.h> #include <media/tveeprom.h> /* If you have already X v4l cards, then set this to X. This way @@ -75,7 +75,7 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static unsigned cardtype_c = 1; static unsigned tuner_c = 1; -static bool radio_c = 1; +static unsigned radio_c = 1; static char pal[] = "--"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -110,7 +110,7 @@ static int retry_mmio = 1; int cx18_debug; module_param_array(tuner, int, &tuner_c, 0644); -module_param_array(radio, bool, &radio_c, 0644); +module_param_array(radio, int, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); @@ -812,7 +812,7 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, CX18_ERR("Can't enable device %d!\n", cx->instance); return -EIO; } - if (pci_set_dma_mask(pci_dev, 0xffffffff)) { + if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { CX18_ERR("No suitable DMA available, card %d\n", cx->instance); return -EIO; } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index b9a94fc..7a37e0e 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -44,8 +44,6 @@ #include <linux/slab.h> #include <asm/byteorder.h> -#include <linux/dvb/video.h> -#include <linux/dvb/audio.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 66b1c15..be49f68 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1085,8 +1085,6 @@ static int cx18_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - CX18_INFO("================= START STATUS CARD #%d " - "=================\n", cx->instance); CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name); if (cx->hw_flags & CX18_HW_TVEEPROM) { struct tveeprom tv; @@ -1120,8 +1118,6 @@ static int cx18_log_status(struct file *file, void *fh) CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", (long long)cx->mpg_data_received, (long long)cx->vbi_data_inserted); - CX18_INFO("================== END STATUS CARD #%d " - "==================\n", cx->instance); return 0; } diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index f8f0e59..d4327da 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -1686,7 +1686,6 @@ static struct v4l2_capability pvr_capability = { .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE), - .reserved = {0, 0, 0, 0} }; static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 875a7ce..8ed460d 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -861,7 +861,6 @@ void cx231xx_release_resources(struct cx231xx *dev) kfree(dev->sliced_cc_mode.alt_max_pkt_size); kfree(dev->ts1_mode.alt_max_pkt_size); kfree(dev); - dev = NULL; } /* diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 829a41b..7f916f0 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2319,8 +2319,7 @@ static int cx231xx_v4l2_close(struct file *filp) if (dev->state & DEV_DISCONNECTED) { if (atomic_read(&dev->devlist_count) > 0) { cx231xx_release_resources(dev); - kfree(dev); - dev = NULL; + fh->dev = NULL; return 0; } return 0; @@ -2350,8 +2349,7 @@ static int cx231xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { cx231xx_release_resources(dev); - kfree(dev); - dev = NULL; + fh->dev = NULL; return 0; } diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c index f617474..7930ca5 100644 --- a/drivers/media/video/cx25821/cx25821-core.c +++ b/drivers/media/video/cx25821/cx25821-core.c @@ -1474,8 +1474,13 @@ static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = { .device = 0x8210, .subvendor = 0x14f1, .subdevice = 0x0920, - }, - { + }, { + /* CX25821 No Brand */ + .vendor = 0x14f1, + .device = 0x8210, + .subvendor = 0x0000, + .subdevice = 0x0000, + }, { /* --- end of list --- */ } }; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 05247d4..fc1ff69 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -5301,15 +5301,4 @@ static struct i2c_driver cx25840_driver = { .id_table = cx25840_id, }; -static __init int init_cx25840(void) -{ - return i2c_add_driver(&cx25840_driver); -} - -static __exit void exit_cx25840(void) -{ - i2c_del_driver(&cx25840_driver); -} - -module_init(init_cx25840); -module_exit(exit_cx25840); +module_i2c_driver(cx25840_driver); diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c index f83baf3..5b68847 100644 --- a/drivers/media/video/davinci/dm355_ccdc.c +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -292,7 +292,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) if ((ccdcparam->med_filt_thres < 0) || (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { dev_dbg(ccdc_cfg.dev, - "Invalid value of median filter thresold\n"); + "Invalid value of median filter threshold\n"); return -EINVAL; } diff --git a/drivers/media/video/davinci/isif.c b/drivers/media/video/davinci/isif.c index 1e63852..5278fe7 100644 --- a/drivers/media/video/davinci/isif.c +++ b/drivers/media/video/davinci/isif.c @@ -34,6 +34,7 @@ #include <linux/videodev2.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/module.h> #include <mach/mux.h> diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c index d6488b7..bba299d 100644 --- a/drivers/media/video/davinci/vpbe_osd.c +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -28,7 +28,6 @@ #include <linux/clk.h> #include <linux/slab.h> -#include <mach/io.h> #include <mach/cputype.h> #include <mach/hardware.h> diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c index 00e80f5..b21ecc8 100644 --- a/drivers/media/video/davinci/vpbe_venc.c +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -27,7 +27,6 @@ #include <mach/hardware.h> #include <mach/mux.h> -#include <mach/io.h> #include <mach/i2c.h> #include <linux/io.h> diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index 25036cb..8bcac65 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -18,8 +18,6 @@ #include <linux/io.h> #include <linux/videodev2.h> -#include <mach/hardware.h> -#include <mach/dm646x.h> #include <media/davinci/vpif_types.h> /* Maximum channel allowed */ diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 286f029..7fa34b4 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -39,8 +39,6 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> -#include <mach/dm646x.h> - #include "vpif_display.h" #include "vpif.h" diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 4561cd8..9fd8cc7 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -353,6 +353,44 @@ static struct em28xx_reg_seq hauppauge_930c_digital[] = { }; #endif +/* 1b80:e425 MaxMedia UB425-TC + * GPIO_6 - demod reset, 0=active + * GPIO_7 - LED, 0=active + */ +static struct em28xx_reg_seq maxmedia_ub425_tc[] = { + {EM2874_R80_GPIO, 0x83, 0xff, 100}, + {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */ + {-1, -1, -1, -1}, +}; + +/* 2304:0242 PCTV QuatroStick (510e) + * GPIO_2: decoder reset, 0=active + * GPIO_4: decoder suspend, 0=active + * GPIO_6: demod reset, 0=active + * GPIO_7: LED, 1=active + */ +static struct em28xx_reg_seq pctv_510e[] = { + {EM2874_R80_GPIO, 0x10, 0xff, 100}, + {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ + {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ + { -1, -1, -1, -1}, +}; + +/* 2013:0251 PCTV QuatroStick nano (520e) + * GPIO_2: decoder reset, 0=active + * GPIO_4: decoder suspend, 0=active + * GPIO_6: demod reset, 0=active + * GPIO_7: LED, 1=active + */ +static struct em28xx_reg_seq pctv_520e[] = { + {EM2874_R80_GPIO, 0x10, 0xff, 100}, + {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */ + {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */ + {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */ + { -1, -1, -1, -1}, +}; + /* * Board definitions */ @@ -1908,6 +1946,41 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + /* 1b80:e425 MaxMedia UB425-TC + * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */ + [EM2874_BOARD_MAXMEDIA_UB425_TC] = { + .name = "MaxMedia UB425-TC", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = maxmedia_ub425_tc, + .has_dvb = 1, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + /* 2304:0242 PCTV QuatroStick (510e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + [EM2884_BOARD_PCTV_510E] = { + .name = "PCTV QuatroStick (510e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_510e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, + /* 2013:0251 PCTV QuatroStick nano (520e) + * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */ + [EM2884_BOARD_PCTV_520E] = { + .name = "PCTV QuatroStick nano (520e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_520e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -2059,6 +2132,12 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */ .driver_info = EM2860_BOARD_EASYCAP }, + { USB_DEVICE(0x1b80, 0xe425), + .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC }, + { USB_DEVICE(0x2304, 0x0242), + .driver_info = EM2884_BOARD_PCTV_510E }, + { USB_DEVICE(0x2013, 0x0251), + .driver_info = EM2884_BOARD_PCTV_520E }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -3122,7 +3201,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, int i, nr; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; - char descr[255] = ""; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -3227,21 +3305,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, speed = "unknown"; } - if (udev->manufacturer) - strlcpy(descr, udev->manufacturer, sizeof(descr)); - - if (udev->product) { - if (*descr) - strlcat(descr, " ", sizeof(descr)); - strlcat(descr, udev->product, sizeof(descr)); - } - - if (*descr) - strlcat(descr, " ", sizeof(descr)); - printk(KERN_INFO DRIVER_NAME - ": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n", - descr, + ": New device %s %s @ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + udev->manufacturer ? udev->manufacturer : "", + udev->product ? udev->product : "", speed, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), @@ -3307,6 +3375,17 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } + if (has_dvb) { + /* pre-allocate DVB isoc transfer buffers */ + retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, + EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size); + if (retval) { + goto unlock_and_free; + } + } + request_modules(dev); /* Should be the last thing to do, to avoid newer udev's to @@ -3379,7 +3458,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) video_device_node_name(dev->vdev)); dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, dev->mode); dev->state |= DEV_DISCONNECTED; wake_up_interruptible(&dev->wait_frame); wake_up_interruptible(&dev->wait_stream); @@ -3388,6 +3467,9 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_release_resources(dev); } + /* free DVB isoc buffers */ + em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + mutex_unlock(&dev->lock); em28xx_close_extension(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 0aacc96..53a9fb9 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -666,6 +666,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -961,146 +962,192 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev) +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; dev->isoc_ctl.nfields = -1; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { + if (isoc_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], + isoc_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; + isoc_bufs->urb[i] = NULL; } - dev->isoc_ctl.transfer_buffer[i] = NULL; + isoc_bufs->transfer_buffer[i] = NULL; } - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); + kfree(isoc_bufs->urb); + kfree(isoc_bufs->transfer_buffer); - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; + isoc_bufs->urb = NULL; + isoc_bufs->transfer_buffer = NULL; + isoc_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); /* - * Allocate URBs and start IRQ + * Allocate URBs */ -int em28xx_init_isoc(struct em28xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size) { - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; int i; int sb_size, pipe; struct urb *urb; int j, k; - int rc; - em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); + em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); - dev->isoc_ctl.isoc_copy = isoc_copy; - dev->isoc_ctl.num_bufs = num_bufs; + isoc_bufs->num_bufs = num_bufs; - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { + isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!isoc_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { + isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!isoc_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(dev->isoc_ctl.urb); + kfree(isoc_bufs->urb); return -ENOMEM; } - dev->isoc_ctl.max_pkt_size = max_pkt_size; + isoc_bufs->max_pkt_size = max_pkt_size; + isoc_bufs->num_packets = max_packets; dev->isoc_ctl.vid_buf = NULL; dev->isoc_ctl.vbi_buf = NULL; - sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return -ENOMEM; } - dev->isoc_ctl.urb[i] = urb; + isoc_bufs->urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { + if (!isoc_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return -ENOMEM; } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + memset(isoc_bufs->transfer_buffer[i], 0, sb_size); /* FIXME: this is a hack - should be 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' should also be using 'desc.bInterval' */ pipe = usb_rcvisocpipe(dev->udev, - dev->mode == EM28XX_ANALOG_MODE ? + mode == EM28XX_ANALOG_MODE ? EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); usb_fill_int_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, + isoc_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev, 1); - urb->number_of_packets = max_packets; + urb->number_of_packets = isoc_bufs->num_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; k = 0; - for (j = 0; j < max_packets; j++) { + for (j = 0; j < isoc_bufs->num_packets; j++) { urb->iso_frame_desc[j].offset = k; urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; + isoc_bufs->max_pkt_size; + k += isoc_bufs->max_pkt_size; } } + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); + +/* + * Allocate URBs and start IRQ + */ +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; + int i; + int rc; + int alloc; + + em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + + dev->isoc_ctl.isoc_copy = isoc_copy; + + if (mode == EM28XX_DIGITAL_MODE) { + isoc_bufs = &dev->isoc_ctl.digital_bufs; + /* no need to free/alloc isoc buffers in digital mode */ + alloc = 0; + } else { + isoc_bufs = &dev->isoc_ctl.analog_bufs; + alloc = 1; + } + + if (alloc) { + rc = em28xx_alloc_isoc(dev, mode, max_packets, + num_bufs, max_pkt_size); + if (rc) + return rc; + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + for (i = 0; i < isoc_bufs->num_bufs; i++) { + rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return rc; } } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index aabbf48..503a8d5 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -61,9 +61,6 @@ if (debug >= level) \ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ } while (0) -#define EM28XX_DVB_NUM_BUFS 5 -#define EM28XX_DVB_MAX_PACKETS 64 - struct em28xx_dvb { struct dvb_frontend *fe[2]; @@ -172,20 +169,21 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) max_dvb_packet_size = dev->dvb_max_pkt_size; if (max_dvb_packet_size < 0) return max_dvb_packet_size; - dprintk(1, "Using %d buffers each with %d bytes\n", + dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, + EM28XX_DVB_MAX_PACKETS, max_dvb_packet_size); - return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, - EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, - em28xx_dvb_isoc_copy); + return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, + EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, + max_dvb_packet_size, em28xx_dvb_isoc_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; - em28xx_uninit_isoc(dev); + em28xx_capture_start(dev, 0); em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -327,6 +325,19 @@ struct drxk_config hauppauge_930c_drxk = { .chunk_size = 56, }; +struct drxk_config maxmedia_ub425_tc_drxk = { + .adr = 0x29, + .single_master = 1, + .no_i2c_bridge = 1, +}; + +struct drxk_config pctv_520e_drxk = { + .adr = 0x29, + .single_master = 1, + .microcode_name = "dvb-demod-drxk-pctv.fw", + .chunk_size = 58, +}; + static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) { struct em28xx_dvb *dvb = fe->sec_priv; @@ -460,6 +471,33 @@ static void terratec_h5_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_h5_end); }; +static void pctv_520e_init(struct em28xx *dev) +{ + /* + * Init TDA8295(?) analog demodulator. Looks like I2C traffic to + * digital demodulator and tuner are routed via TDA8295. + */ + int i; + struct { + unsigned char r[4]; + int len; + } regs[] = { + {{ 0x06, 0x02, 0x00, 0x31 }, 4}, + {{ 0x01, 0x02 }, 2}, + {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0xff, 0xaf }, 4}, + {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, + {{ 0x01, 0x00 }, 2}, + {{ 0x01, 0x00, 0x73, 0xaf }, 4}, + }; + + dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */ + + for (i = 0; i < ARRAY_SIZE(regs); i++) + i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len); +}; + static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) { /* Values extracted from a USB trace of the Terratec Windows driver */ @@ -938,6 +976,48 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap, &em28xx_a8293_config); break; + case EM2874_BOARD_MAXMEDIA_UB425_TC: + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, + &dev->i2c_adap); + + if (dvb->fe[0]) { + /* disable I2C-gate */ + dvb->fe[0]->ops.i2c_gate_ctrl = NULL; + + /* attach tuner */ + if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], + &dev->i2c_adap, 0x60)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + + /* TODO: we need drx-3913k firmware in order to support DVB-T */ + em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \ + "driver version\n"); + + break; + case EM2884_BOARD_PCTV_510E: + case EM2884_BOARD_PCTV_520E: + pctv_520e_init(dev); + + /* attach demodulator */ + dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, + &dev->i2c_adap); + + if (dvb->fe[0]) { + /* attach tuner */ + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap, + &em28xx_cxd2820r_tda18271_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 36f5a9b..a88e169 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -41,14 +41,6 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define dprintk1(lvl, fmt, args...) \ -do { \ - if (i2c_debug >= lvl) { \ - printk(fmt, ##args); \ - } \ -} while (0) - #define dprintk2(lvl, fmt, args...) \ do { \ if (i2c_debug >= lvl) { \ diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 613300b..324b695 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -760,17 +760,19 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, goto fail; } - if (!dev->isoc_ctl.num_bufs) + if (!dev->isoc_ctl.analog_bufs.num_bufs) urb_init = 1; if (urb_init) { if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, + EM28XX_NUM_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy_vbi); else - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, + EM28XX_NUM_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy); @@ -2267,7 +2269,7 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 22e252b..2868b19 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -125,6 +125,9 @@ #define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81 #define EM2884_BOARD_CINERGY_HTC_STICK 82 #define EM2860_BOARD_HT_VIDBOX_NW03 83 +#define EM2874_BOARD_MAXMEDIA_UB425_TC 84 +#define EM2884_BOARD_PCTV_510E 85 +#define EM2884_BOARD_PCTV_520E 86 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -151,12 +154,14 @@ /* number of buffers for isoc transfers */ #define EM28XX_NUM_BUFS 5 +#define EM28XX_DVB_NUM_BUFS 5 /* number of packets for each buffer 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 64 +#define EM28XX_DVB_MAX_PACKETS 64 #define EM28XX_INTERLACED_DEFAULT 1 @@ -197,10 +202,13 @@ enum em28xx_mode { struct em28xx; -struct em28xx_usb_isoc_ctl { +struct em28xx_usb_isoc_bufs { /* max packet size of isoc transaction */ int max_pkt_size; + /* number of packets in each buffer */ + int num_packets; + /* number of allocated urbs */ int num_bufs; @@ -209,6 +217,14 @@ struct em28xx_usb_isoc_ctl { /* transfer buffers for isoc transfer */ char **transfer_buffer; +}; + +struct em28xx_usb_isoc_ctl { + /* isoc transfer buffers for analog mode */ + struct em28xx_usb_isoc_bufs analog_bufs; + + /* isoc transfer buffers for digital mode */ + struct em28xx_usb_isoc_bufs digital_bufs; /* Last buffer command and region */ u8 cmd; @@ -600,9 +616,6 @@ struct em28xx { unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ int dvb_alt; /* alternate for DVB */ unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ - struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ - char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc - transfer */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ @@ -676,10 +689,12 @@ int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -int em28xx_init_isoc(struct em28xx *dev, int max_packets, - int num_bufs, int max_pkt_size, +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size); +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev); +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile index f511ecc..773ea342 100644 --- a/drivers/media/video/gspca/gl860/Makefile +++ b/drivers/media/video/gspca/gl860/Makefile @@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \ gl860-ov9655.o \ gl860-mi2020.o -ccflags-y += -Idrivers/media/video/gspca +ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile index 7f52961..575b75b 100644 --- a/drivers/media/video/gspca/m5602/Makefile +++ b/drivers/media/video/gspca/m5602/Makefile @@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \ m5602_s5k83a.o \ m5602_s5k4aa.o -ccflags-y += -Idrivers/media/video/gspca +ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c index fbfa02a..e6601b8 100644 --- a/drivers/media/video/gspca/ov534_9.c +++ b/drivers/media/video/gspca/ov534_9.c @@ -1107,16 +1107,34 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; + s8 sval; if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS)) return; - val = sd->ctrls[BRIGHTNESS].val; - if (val < 8) - val = 15 - val; /* f .. 8 */ - else - val = val - 8; /* 0 .. 7 */ - sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ - 0x0f | (val << 4)); + if (sd->sensor == SENSOR_OV562x) { + sval = sd->ctrls[BRIGHTNESS].val; + val = 0x76; + val += sval; + sccb_write(gspca_dev, 0x24, val); + val = 0x6a; + val += sval; + sccb_write(gspca_dev, 0x25, val); + if (sval < -40) + val = 0x71; + else if (sval < 20) + val = 0x94; + else + val = 0xe6; + sccb_write(gspca_dev, 0x26, val); + } else { + val = sd->ctrls[BRIGHTNESS].val; + if (val < 8) + val = 15 - val; /* f .. 8 */ + else + val = val - 8; /* 0 .. 7 */ + sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); + } } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1339,7 +1357,16 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x56, 0x17); } else if ((sensor_id & 0xfff0) == 0x5620) { sd->sensor = SENSOR_OV562x; - + gspca_dev->ctrl_dis = (1 << CONTRAST) | + (1 << AUTOGAIN) | + (1 << EXPOSURE) | + (1 << SHARPNESS) | + (1 << SATUR) | + (1 << LIGHTFREQ); + + sd->ctrls[BRIGHTNESS].min = -90; + sd->ctrls[BRIGHTNESS].max = 90; + sd->ctrls[BRIGHTNESS].def = 0; gspca_dev->cam.cam_mode = ov562x_mode; gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode); @@ -1360,8 +1387,12 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor == SENSOR_OV971x || sd->sensor == SENSOR_OV562x) + if (sd->sensor == SENSOR_OV971x) return gspca_dev->usb_err; + else if (sd->sensor == SENSOR_OV562x) { + setbrightness(gspca_dev); + return gspca_dev->usb_err; + } switch (gspca_dev->curr_mode) { case QVGA_MODE: /* 320x240 */ sccb_w_array(gspca_dev, ov965x_start_1_vga, diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 9db2b34..30662fc 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -1,8 +1,8 @@ /* - * Pixart PAC7302 library - * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Pixart PAC7302 driver * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li * * Separated from Pixart PAC7311 library by Márton Németh * Camera button input handling by Márton Németh <nm127@freemail.hu> @@ -63,67 +63,61 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define MODULE_NAME "pac7302" - #include <linux/input.h> #include <media/v4l2-chip-ident.h> #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" -MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, " + "Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7302"); MODULE_LICENSE("GPL"); +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + COLORS, + WHITE_BALANCE, + RED_BALANCE, + BLUE_BALANCE, + GAIN, + AUTOGAIN, + EXPOSURE, + VFLIP, + HFLIP, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor for pac7302 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char colors; - unsigned char white_balance; - unsigned char red_balance; - unsigned char blue_balance; - unsigned char gain; - unsigned char autogain; - unsigned short exposure; - __u8 hflip; - __u8 vflip; + struct gspca_ctrl ctrls[NCTRLS]; + u8 flags; #define FL_HFLIP 0x01 /* mirrored by default */ #define FL_VFLIP 0x02 /* vertical flipped by default */ u8 sof_read; - u8 autogain_ignore_frames; + s8 autogain_ignore_frames; atomic_t avg_lum; }; /* 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_setcolors(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcolors(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_setredbalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbluebalance(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_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_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 void setbrightcont(struct gspca_dev *gspca_dev); +static void setcolors(struct gspca_dev *gspca_dev); +static void setwhitebalance(struct gspca_dev *gspca_dev); +static void setredbalance(struct gspca_dev *gspca_dev); +static void setbluebalance(struct gspca_dev *gspca_dev); +static void setgain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static void setautogain(struct gspca_dev *gspca_dev); +static void sethvflip(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { - { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -132,13 +126,11 @@ static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_MAX 0x20 .maximum = BRIGHTNESS_MAX, .step = 1, -#define BRIGHTNESS_DEF 0x10 - .default_value = BRIGHTNESS_DEF, + .default_value = 0x10, }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = setbrightcont }, - { +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -147,13 +139,11 @@ static const struct ctrl sd_ctrls[] = { #define CONTRAST_MAX 255 .maximum = CONTRAST_MAX, .step = 1, -#define CONTRAST_DEF 127 - .default_value = CONTRAST_DEF, + .default_value = 127, }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = setbrightcont }, - { +[COLORS] = { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, @@ -162,13 +152,11 @@ static const struct ctrl sd_ctrls[] = { #define COLOR_MAX 255 .maximum = COLOR_MAX, .step = 1, -#define COLOR_DEF 127 - .default_value = COLOR_DEF, + .default_value = 127 }, - .set = sd_setcolors, - .get = sd_getcolors, + .set_control = setcolors }, - { +[WHITE_BALANCE] = { { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -176,13 +164,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, -#define WHITEBALANCE_DEF 4 - .default_value = WHITEBALANCE_DEF, + .default_value = 4, }, - .set = sd_setwhitebalance, - .get = sd_getwhitebalance, + .set_control = setwhitebalance }, - { +[RED_BALANCE] = { { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -190,13 +176,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 3, .step = 1, -#define REDBALANCE_DEF 1 - .default_value = REDBALANCE_DEF, + .default_value = 1, }, - .set = sd_setredbalance, - .get = sd_getredbalance, + .set_control = setredbalance }, - { +[BLUE_BALANCE] = { { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -204,29 +188,25 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 3, .step = 1, -#define BLUEBALANCE_DEF 1 - .default_value = BLUEBALANCE_DEF, + .default_value = 1, }, - .set = sd_setbluebalance, - .get = sd_getbluebalance, + .set_control = setbluebalance }, - { +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, -#define GAIN_MAX 255 - .maximum = GAIN_MAX, + .maximum = 255, .step = 1, #define GAIN_DEF 127 #define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ .default_value = GAIN_DEF, }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = setgain }, - { +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -238,10 +218,9 @@ static const struct ctrl sd_ctrls[] = { #define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ .default_value = EXPOSURE_DEF, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = setexposure }, - { +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -252,10 +231,9 @@ static const struct ctrl sd_ctrls[] = { #define AUTOGAIN_DEF 1 .default_value = AUTOGAIN_DEF, }, - .set = sd_setautogain, - .get = sd_getautogain, + .set_control = setautogain, }, - { +[HFLIP] = { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -263,13 +241,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define HFLIP_DEF 0 - .default_value = HFLIP_DEF, + .default_value = 0, }, - .set = sd_sethflip, - .get = sd_gethflip, + .set_control = sethvflip, }, - { +[VFLIP] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -277,11 +253,9 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 - .default_value = VFLIP_DEF, + .default_value = 0, }, - .set = sd_setvflip, - .get = sd_getvflip, + .set_control = sethvflip }, }; @@ -290,21 +264,21 @@ static const struct v4l2_pix_format vga_mode[] = { .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 0}, + }, }; #define LOAD_PAGE3 255 #define END_OF_SEQUENCE 0 /* pac 7302 */ -static const __u8 init_7302[] = { +static const u8 init_7302[] = { /* index,value */ 0xff, 0x01, /* page 1 */ 0x78, 0x00, /* deactivate */ 0xff, 0x01, 0x78, 0x40, /* led off */ }; -static const __u8 start_7302[] = { +static const u8 start_7302[] = { /* index, len, [value]* */ 0xff, 1, 0x00, /* page 0 */ 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, @@ -319,7 +293,7 @@ static const __u8 start_7302[] = { 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, 0x00, 0x54, 0x11, 0x55, 1, 0x00, - 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, 0x6b, 1, 0x00, 0x6e, 3, 0x08, 0x06, 0x00, 0x72, 3, 0x00, 0xff, 0x00, @@ -370,7 +344,7 @@ static const __u8 start_7302[] = { #define SKIP 0xaa /* page 3 - the value SKIP says skip the index - see reg_w_page() */ -static const __u8 page3_7302[] = { +static const u8 page3_7302[] = { 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16, 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -394,7 +368,7 @@ static const __u8 page3_7302[] = { }; static void reg_w_buf(struct gspca_dev *gspca_dev, - __u8 index, + u8 index, const u8 *buffer, int len) { int ret; @@ -410,7 +384,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); if (ret < 0) { - pr_err("reg_w_buf failed index 0x%02x, error %d\n", + pr_err("reg_w_buf failed i: %02x error %d\n", index, ret); gspca_dev->usb_err = ret; } @@ -418,8 +392,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, static void reg_w(struct gspca_dev *gspca_dev, - __u8 index, - __u8 value) + u8 index, + u8 value) { int ret; @@ -433,14 +407,14 @@ static void reg_w(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n", + pr_err("reg_w() failed i: %02x v: %02x error %d\n", index, value, ret); gspca_dev->usb_err = ret; } } static void reg_w_seq(struct gspca_dev *gspca_dev, - const __u8 *seq, int len) + const u8 *seq, int len) { while (--len >= 0) { reg_w(gspca_dev, seq[0], seq[1]); @@ -450,7 +424,7 @@ static void reg_w_seq(struct gspca_dev *gspca_dev, /* load the beginning of a page */ static void reg_w_page(struct gspca_dev *gspca_dev, - const __u8 *page, int len) + const u8 *page, int len) { int index; int ret = 0; @@ -468,7 +442,7 @@ static void reg_w_page(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n", + pr_err("reg_w_page() failed i: %02x v: %02x error %d\n", index, page[index], ret); gspca_dev->usb_err = ret; break; @@ -478,8 +452,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev, /* output a variable sequence */ static void reg_w_var(struct gspca_dev *gspca_dev, - const __u8 *seq, - const __u8 *page3, unsigned int page3_len) + const u8 *seq, + const u8 *page3, unsigned int page3_len) { int index, len; @@ -493,11 +467,13 @@ static void reg_w_var(struct gspca_dev *gspca_dev, reg_w_page(gspca_dev, page3, page3_len); break; default: +#ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, "Incorrect variable sequence"); return; } +#endif while (len > 0) { if (len < 8) { reg_w_buf(gspca_dev, @@ -524,21 +500,11 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; - PDEBUG(D_CONF, "Find Sensor PAC7302"); cam->cam_mode = vga_mode; /* only 640x480 */ cam->nmodes = ARRAY_SIZE(vga_mode); - sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; - sd->colors = COLOR_DEF; - sd->white_balance = WHITEBALANCE_DEF; - sd->red_balance = REDBALANCE_DEF; - sd->blue_balance = BLUEBALANCE_DEF; - sd->gain = GAIN_DEF; - sd->exposure = EXPOSURE_DEF; - sd->autogain = AUTOGAIN_DEF; - sd->hflip = HFLIP_DEF; - sd->vflip = VFLIP_DEF; + gspca_dev->cam.ctrls = sd->ctrls; + sd->flags = id->driver_info; return 0; } @@ -548,19 +514,19 @@ static void setbrightcont(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, v; - static const __u8 max[10] = + static const u8 max[10] = {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, 0xd4, 0xec}; - static const __u8 delta[10] = + static const u8 delta[10] = {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, 0x11, 0x0b}; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 10; i++) { v = max[i]; - v += (sd->brightness - BRIGHTNESS_MAX) + v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX) * 150 / BRIGHTNESS_MAX; /* 200 ? */ - v -= delta[i] * sd->contrast / CONTRAST_MAX; + v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX; if (v < 0) v = 0; else if (v > 0xff) @@ -584,12 +550,11 @@ static void setcolors(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 9; i++) { - v = a[i] * sd->colors / COLOR_MAX + b[i]; + v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i]; reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); reg_w(gspca_dev, 0x0f + 2 * i + 1, v); } reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); } static void setwhitebalance(struct gspca_dev *gspca_dev) @@ -597,10 +562,9 @@ static void setwhitebalance(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc6, sd->white_balance); + reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance); } static void setredbalance(struct gspca_dev *gspca_dev) @@ -608,10 +572,9 @@ static void setredbalance(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc5, sd->red_balance); + reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance); } static void setbluebalance(struct gspca_dev *gspca_dev) @@ -619,10 +582,9 @@ static void setbluebalance(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xc7, sd->blue_balance); + reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance); } static void setgain(struct gspca_dev *gspca_dev) @@ -630,7 +592,7 @@ static void setgain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->gain >> 3); + reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -639,13 +601,13 @@ static void setgain(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 clockdiv; - __u16 exposure; + u8 clockdiv; + u16 exposure; /* register 2 of frame 3 contains the clock divider configuring the no fps according to the formula: 90 / reg. sd->exposure is the desired exposure time in 0.5 ms. */ - clockdiv = (90 * sd->exposure + 1999) / 2000; + clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000; /* Note clockdiv = 3 also works, but when running at 30 fps, depending on the scene being recorded, the camera switches to another @@ -664,7 +626,7 @@ static void setexposure(struct gspca_dev *gspca_dev) /* frame exposure time in ms = 1000 * clockdiv / 90 -> exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ - exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv); + exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv); /* 0 = use full frametime, 448 = no exposure, reverse it */ exposure = 448 - exposure; @@ -677,15 +639,35 @@ static void setexposure(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); } +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->ctrls[AUTOGAIN].val) { + sd->ctrls[EXPOSURE].val = EXPOSURE_DEF; + sd->ctrls[GAIN].val = GAIN_DEF; + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + } else { + sd->autogain_ignore_frames = -1; + } + setexposure(gspca_dev); + setgain(gspca_dev); +} + static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 data, hflip, vflip; - hflip = sd->hflip; + hflip = sd->ctrls[HFLIP].val; if (sd->flags & FL_HFLIP) hflip = !hflip; - vflip = sd->vflip; + vflip = sd->ctrls[VFLIP].val; if (sd->flags & FL_VFLIP) vflip = !vflip; @@ -708,8 +690,6 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sd->sof_read = 0; - reg_w_var(gspca_dev, start_7302, page3_7302, sizeof(page3_7302)); setbrightcont(gspca_dev); @@ -717,15 +697,13 @@ static int sd_start(struct gspca_dev *gspca_dev) setwhitebalance(gspca_dev); setredbalance(gspca_dev); setbluebalance(gspca_dev); - setgain(gspca_dev); - setexposure(gspca_dev); + setautogain(gspca_dev); sethvflip(gspca_dev); /* only resolution 640x480 is supported for pac7302 */ sd->sof_read = 0; - sd->autogain_ignore_frames = 0; - atomic_set(&sd->avg_lum, -1); + atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val); /* start stream */ reg_w(gspca_dev, 0xff, 0x01); @@ -751,8 +729,10 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x40); } -/* Include pac common sof detection functions */ -#include "pac_common.h" +/* !! coarse_grained_expo_autogain is not used !! */ +#define exp_too_low_cnt flags +#define exp_too_high_cnt sof_read +#include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) { @@ -761,65 +741,44 @@ static void do_autogain(struct gspca_dev *gspca_dev) int desired_lum; const int deadzone = 30; - if (avg_lum == -1) + if (sd->autogain_ignore_frames < 0) return; - desired_lum = 270 + sd->brightness; - - if (sd->autogain_ignore_frames > 0) + if (sd->autogain_ignore_frames > 0) { sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, - deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + } else { + desired_lum = 270 + sd->ctrls[BRIGHTNESS].val; + + auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE); sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } } -/* JPEG header, part 1 */ -static const unsigned char pac_jpeg_header1[] = { - 0xff, 0xd8, /* SOI: Start of Image */ - - 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ - 0x00, 0x11, /* length = 17 bytes (including this length field) */ - 0x08 /* Precision: 8 */ - /* 2 bytes is placed here: number of image lines */ - /* 2 bytes is placed here: samples per line */ -}; - -/* JPEG header, continued */ -static const unsigned char pac_jpeg_header2[] = { - 0x03, /* Number of image components: 3 */ - 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ - 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ - 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ - - 0xff, 0xda, /* SOS: Start Of Scan */ - 0x00, 0x0c, /* length = 12 bytes (including this length field) */ - 0x03, /* number of components: 3 */ - 0x01, 0x00, /* selector 1, table 0x00 */ - 0x02, 0x11, /* selector 2, table 0x11 */ - 0x03, 0x11, /* selector 3, table 0x11 */ - 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ - 0x00 /* Successive approximation: 0 */ +/* JPEG header */ +static const u8 jpeg_header[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08, /* Precision: 8 */ + 0x02, 0x80, /* height = 640 (image rotated) */ + 0x01, 0xe0, /* width = 480 */ + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ }; -static void pac_start_frame(struct gspca_dev *gspca_dev, - __u16 lines, __u16 samples_per_line) -{ - unsigned char tmpbuf[4]; - - gspca_frame_add(gspca_dev, FIRST_PACKET, - pac_jpeg_header1, sizeof(pac_jpeg_header1)); - - tmpbuf[0] = lines >> 8; - tmpbuf[1] = lines & 0xff; - tmpbuf[2] = samples_per_line >> 8; - tmpbuf[3] = samples_per_line & 0xff; - - gspca_frame_add(gspca_dev, INTER_PACKET, - tmpbuf, sizeof(tmpbuf)); - gspca_frame_add(gspca_dev, INTER_PACKET, - pac_jpeg_header2, sizeof(pac_jpeg_header2)); -} - /* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ @@ -827,7 +786,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; u8 *image; - unsigned char *sof; + u8 *sof; sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { @@ -864,234 +823,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n >= lum_offset) atomic_set(&sd->avg_lum, data[-lum_offset] + data[-lum_offset + 1]); - else - atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ /* The PAC7302 has the image rotated 90 degrees */ - pac_start_frame(gspca_dev, - gspca_dev->width, gspca_dev->height); + gspca_frame_add(gspca_dev, FIRST_PACKET, + jpeg_header, sizeof jpeg_header); } 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) - setbrightcont(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setbrightcont(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->colors = val; - if (gspca_dev->streaming) - setcolors(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->colors; - return 0; -} - -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->white_balance = val; - if (gspca_dev->streaming) - setwhitebalance(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->white_balance; - return 0; -} - -static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->red_balance = val; - if (gspca_dev->streaming) - setredbalance(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->red_balance; - return 0; -} - -static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->blue_balance = val; - if (gspca_dev->streaming) - setbluebalance(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->blue_balance; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - if (sd->autogain) { - sd->exposure = EXPOSURE_DEF; - sd->gain = GAIN_DEF; - if (gspca_dev->streaming) { - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - setexposure(gspca_dev); - setgain(gspca_dev); - } - } - - return gspca_dev->usb_err; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->hflip; - return 0; -} - -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->vflip; - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int sd_dbg_s_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { - __u8 index; - __u8 value; + u8 index; + u8 value; /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit long on the USB bus) @@ -1103,8 +849,8 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, ) { /* Currently writing to page 0 is only supported. */ /* reg_w() only supports 8bit index */ - index = reg->reg & 0x000000ff; - value = reg->val & 0x000000ff; + index = reg->reg; + value = reg->val; /* Note that there shall be no access to other page by any other function between the page swith and @@ -1165,7 +911,7 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description for pac7302 */ static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, @@ -1187,6 +933,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3009)}, + {USB_DEVICE(0x06f8, 0x301b)}, {USB_DEVICE(0x093a, 0x2620)}, {USB_DEVICE(0x093a, 0x2621)}, {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, @@ -1211,7 +958,7 @@ static int sd_probe(struct usb_interface *intf, } static struct usb_driver sd_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 9e198b4..7e71aa2 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -1,5 +1,7 @@ /* * Sonix sn9c201 sn9c202 library + * + * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr> * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com> * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com> * @@ -33,8 +35,6 @@ MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, " MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver"); MODULE_LICENSE("GPL"); -#define MODULE_NAME "sn9c20x" - /* * Pixel format private data */ @@ -66,10 +66,37 @@ MODULE_LICENSE("GPL"); #define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ #define FLIP_DETECT 0x4 +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + SATURATION, + HUE, + GAMMA, + BLUE, + RED, + VFLIP, + HFLIP, + EXPOSURE, + GAIN, + AUTOGAIN, + QUALITY, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; + struct gspca_ctrl ctrls[NCTRLS]; + + struct work_struct work; + struct workqueue_struct *work_thread; + + u32 pktsz; /* (used by pkt_scan) */ + u16 npkt; + s8 nchg; + u8 fmt; /* (used for JPEG QTAB update */ + #define MIN_AVG_LUM 80 #define MAX_AVG_LUM 130 atomic_t avg_lum; @@ -77,31 +104,18 @@ struct sd { u8 older_step; u8 exposure_step; - u8 brightness; - u8 contrast; - u8 saturation; - s16 hue; - u8 gamma; - u8 red; - u8 blue; - - u8 hflip; - u8 vflip; - u8 gain; - u16 exposure; - u8 auto_exposure; - u8 i2c_addr; u8 sensor; u8 hstart; u8 vstart; u8 jpeg_hdr[JPEG_HDR_SZ]; - u8 quality; u8 flags; }; +static void qual_upd(struct work_struct *work); + struct i2c_reg_u8 { u8 reg; u8 val; @@ -112,31 +126,6 @@ struct i2c_reg_u16 { u16 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_setsaturation(struct gspca_dev *gspca_dev, s32 val); -static int sd_getsaturation(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_setgamma(struct gspca_dev *gspca_dev, s32 val); -static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val); -static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val); -static int sd_getbluebalance(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_sethflip(struct gspca_dev *gspca_dev, s32 val); -static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); -static int sd_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", @@ -177,9 +166,16 @@ static const struct dmi_system_id flip_dmi_table[] = { {} }; -static const struct ctrl sd_ctrls[] = { - { -#define BRIGHTNESS_IDX 0 +static void set_cmatrix(struct gspca_dev *gspca_dev); +static void set_gamma(struct gspca_dev *gspca_dev); +static void set_redblue(struct gspca_dev *gspca_dev); +static void set_hvflip(struct gspca_dev *gspca_dev); +static void set_exposure(struct gspca_dev *gspca_dev); +static void set_gain(struct gspca_dev *gspca_dev); +static void set_quality(struct gspca_dev *gspca_dev); + +static const struct ctrl sd_ctrls[NCTRLS] = { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -187,14 +183,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define BRIGHTNESS_DEFAULT 0x7f - .default_value = BRIGHTNESS_DEFAULT, + .default_value = 0x7f }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = set_cmatrix }, - { -#define CONTRAST_IDX 1 +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -202,14 +195,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define CONTRAST_DEFAULT 0x7f - .default_value = CONTRAST_DEFAULT, + .default_value = 0x7f }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = set_cmatrix }, - { -#define SATURATION_IDX 2 +[SATURATION] = { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, @@ -217,14 +207,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define SATURATION_DEFAULT 0x7f - .default_value = SATURATION_DEFAULT, + .default_value = 0x7f }, - .set = sd_setsaturation, - .get = sd_getsaturation, + .set_control = set_cmatrix }, - { -#define HUE_IDX 3 +[HUE] = { { .id = V4L2_CID_HUE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -232,14 +219,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = -180, .maximum = 180, .step = 1, -#define HUE_DEFAULT 0 - .default_value = HUE_DEFAULT, + .default_value = 0 }, - .set = sd_sethue, - .get = sd_gethue, + .set_control = set_cmatrix }, - { -#define GAMMA_IDX 4 +[GAMMA] = { { .id = V4L2_CID_GAMMA, .type = V4L2_CTRL_TYPE_INTEGER, @@ -247,14 +231,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, -#define GAMMA_DEFAULT 0x10 - .default_value = GAMMA_DEFAULT, + .default_value = 0x10 }, - .set = sd_setgamma, - .get = sd_getgamma, + .set_control = set_gamma }, - { -#define BLUE_IDX 5 +[BLUE] = { { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -262,14 +243,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x7f, .step = 1, -#define BLUE_DEFAULT 0x28 - .default_value = BLUE_DEFAULT, + .default_value = 0x28 }, - .set = sd_setbluebalance, - .get = sd_getbluebalance, + .set_control = set_redblue }, - { -#define RED_IDX 6 +[RED] = { { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -277,14 +255,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x7f, .step = 1, -#define RED_DEFAULT 0x28 - .default_value = RED_DEFAULT, + .default_value = 0x28 }, - .set = sd_setredbalance, - .get = sd_getredbalance, + .set_control = set_redblue }, - { -#define HFLIP_IDX 7 +[HFLIP] = { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -292,14 +267,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define HFLIP_DEFAULT 0 - .default_value = HFLIP_DEFAULT, + .default_value = 0, }, - .set = sd_sethflip, - .get = sd_gethflip, + .set_control = set_hvflip }, - { -#define VFLIP_IDX 8 +[VFLIP] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -307,14 +279,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEFAULT 0 - .default_value = VFLIP_DEFAULT, + .default_value = 0, }, - .set = sd_setvflip, - .get = sd_getvflip, + .set_control = set_hvflip }, - { -#define EXPOSURE_IDX 9 +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -322,14 +291,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x1780, .step = 1, -#define EXPOSURE_DEFAULT 0x33 - .default_value = EXPOSURE_DEFAULT, + .default_value = 0x33, }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = set_exposure }, - { -#define GAIN_IDX 10 +[GAIN] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -337,14 +303,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 28, .step = 1, -#define GAIN_DEFAULT 0x00 - .default_value = GAIN_DEFAULT, + .default_value = 0, }, - .set = sd_setgain, - .get = sd_getgain, + .set_control = set_gain }, - { -#define AUTOGAIN_IDX 11 +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -352,11 +315,23 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AUTO_EXPOSURE_DEFAULT 1 - .default_value = AUTO_EXPOSURE_DEFAULT, + .default_value = 1, + }, + }, +[QUALITY] = { + { + .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Compression Quality", +#define QUALITY_MIN 50 +#define QUALITY_MAX 90 +#define QUALITY_DEF 80 + .minimum = QUALITY_MIN, + .maximum = QUALITY_MAX, + .step = 1, + .default_value = QUALITY_DEF, }, - .set = sd_setautoexposure, - .get = sd_getautoexposure, + .set_control = set_quality }, }; @@ -876,7 +851,7 @@ static u8 hv7131r_gain[] = { }; static struct i2c_reg_u8 soi968_init[] = { - {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, + {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, @@ -902,7 +877,7 @@ static struct i2c_reg_u8 ov7660_init[] = { }; static struct i2c_reg_u8 ov7670_init[] = { - {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, + {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00}, @@ -959,7 +934,7 @@ static struct i2c_reg_u8 ov7670_init[] = { }; static struct i2c_reg_u8 ov9650_init[] = { - {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, + {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00}, @@ -989,7 +964,7 @@ static struct i2c_reg_u8 ov9650_init[] = { }; static struct i2c_reg_u8 ov9655_init[] = { - {0x12, 0x80}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, + {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}, @@ -1112,10 +1087,13 @@ static struct i2c_reg_u8 hv7131r_init[] = { {0x23, 0x09}, {0x01, 0x08}, }; -static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) { struct usb_device *dev = gspca_dev->dev; int result; + + if (gspca_dev->usb_err < 0) + return; result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, @@ -1125,17 +1103,19 @@ static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) length, 500); if (unlikely(result < 0 || result != length)) { - pr_err("Read register failed 0x%02X\n", reg); - return -EIO; + pr_err("Read register %02x failed %d\n", reg, result); + gspca_dev->usb_err = result; } - return 0; } -static int reg_w(struct gspca_dev *gspca_dev, u16 reg, +static void reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) { struct usb_device *dev = gspca_dev->dev; int result; + + if (gspca_dev->usb_err < 0) + return; memcpy(gspca_dev->usb_buf, buffer, length); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x08, @@ -1146,38 +1126,41 @@ static int reg_w(struct gspca_dev *gspca_dev, u16 reg, length, 500); if (unlikely(result < 0 || result != length)) { - pr_err("Write register failed index 0x%02X\n", reg); - return -EIO; + pr_err("Write register %02x failed %d\n", reg, result); + gspca_dev->usb_err = result; } - return 0; } -static int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) { - u8 data[1] = {value}; - return reg_w(gspca_dev, reg, data, 1); + reg_w(gspca_dev, reg, &value, 1); } -static int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) { int i; + reg_w(gspca_dev, 0x10c0, buffer, 8); for (i = 0; i < 5; i++) { reg_r(gspca_dev, 0x10c0, 1); + if (gspca_dev->usb_err < 0) + return; if (gspca_dev->usb_buf[0] & 0x04) { - if (gspca_dev->usb_buf[0] & 0x08) - return -EIO; - return 0; + if (gspca_dev->usb_buf[0] & 0x08) { + pr_err("i2c_w error\n"); + gspca_dev->usb_err = -EIO; + } + return; } - msleep(1); + msleep(10); } - return -EIO; + pr_err("i2c_w reg %02x no response\n", buffer[2]); +/* gspca_dev->usb_err = -EIO; fixme: may occur */ } -static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; - u8 row[8]; /* @@ -1193,10 +1176,19 @@ static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) row[6] = 0x00; row[7] = 0x10; - return i2c_w(gspca_dev, row); + i2c_w(gspca_dev, row); +} + +static void i2c_w1_buf(struct gspca_dev *gspca_dev, + struct i2c_reg_u8 *buf, int sz) +{ + while (--sz >= 0) { + i2c_w1(gspca_dev, buf->reg, buf->val); + buf++; + } } -static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1208,16 +1200,25 @@ static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) row[0] = 0x81 | (3 << 4); row[1] = sd->i2c_addr; row[2] = reg; - row[3] = (val >> 8) & 0xff; - row[4] = val & 0xff; + row[3] = val >> 8; + row[4] = val; row[5] = 0x00; row[6] = 0x00; row[7] = 0x10; - return i2c_w(gspca_dev, row); + i2c_w(gspca_dev, row); +} + +static void i2c_w2_buf(struct gspca_dev *gspca_dev, + struct i2c_reg_u16 *buf, int sz) +{ + while (--sz >= 0) { + i2c_w2(gspca_dev, buf->reg, buf->val); + buf++; + } } -static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) +static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1230,19 +1231,15 @@ static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; + i2c_w(gspca_dev, row); row[0] = 0x81 | (1 << 4) | 0x02; row[2] = 0; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; - if (reg_r(gspca_dev, 0x10c2, 5) < 0) - return -EIO; + i2c_w(gspca_dev, row); + reg_r(gspca_dev, 0x10c2, 5); *val = gspca_dev->usb_buf[4]; - return 0; } -static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) +static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1255,233 +1252,204 @@ static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; + i2c_w(gspca_dev, row); row[0] = 0x81 | (2 << 4) | 0x02; row[2] = 0; - if (i2c_w(gspca_dev, row) < 0) - return -EIO; - if (reg_r(gspca_dev, 0x10c2, 5) < 0) - return -EIO; + i2c_w(gspca_dev, row); + reg_r(gspca_dev, 0x10c2, 5); *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; - return 0; } -static int ov9650_init_sensor(struct gspca_dev *gspca_dev) +static void ov9650_init_sensor(struct gspca_dev *gspca_dev) { - int i; u16 id; struct sd *sd = (struct sd *) gspca_dev; - if (i2c_r2(gspca_dev, 0x1c, &id) < 0) - return -EINVAL; + i2c_r2(gspca_dev, 0x1c, &id); + if (gspca_dev->usb_err < 0) + return; if (id != 0x7fa2) { pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id); - return -ENODEV; + gspca_dev->usb_err = -ENODEV; + return; } - for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { - if (i2c_w1(gspca_dev, ov9650_init[i].reg, - ov9650_init[i].val) < 0) { - pr_err("OV9650 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV9650 sensor initialization failed\n"); sd->hstart = 1; sd->vstart = 7; - return 0; } -static int ov9655_init_sensor(struct gspca_dev *gspca_dev) +static void ov9655_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { - if (i2c_w1(gspca_dev, ov9655_init[i].reg, - ov9655_init[i].val) < 0) { - pr_err("OV9655 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV9655 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 2; - return 0; } -static int soi968_init_sensor(struct gspca_dev *gspca_dev) +static void soi968_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { - if (i2c_w1(gspca_dev, soi968_init[i].reg, - soi968_init[i].val) < 0) { - pr_err("SOI968 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init)); + if (gspca_dev->usb_err < 0) + pr_err("SOI968 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) - | (1 << EXPOSURE_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP) + | (1 << EXPOSURE); sd->hstart = 60; sd->vstart = 11; - return 0; } -static int ov7660_init_sensor(struct gspca_dev *gspca_dev) +static void ov7660_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { - if (i2c_w1(gspca_dev, ov7660_init[i].reg, - ov7660_init[i].val) < 0) { - pr_err("OV7660 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV7660 sensor initialization failed\n"); sd->hstart = 3; sd->vstart = 3; - return 0; } -static int ov7670_init_sensor(struct gspca_dev *gspca_dev) +static void ov7670_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { - if (i2c_w1(gspca_dev, ov7670_init[i].reg, - ov7670_init[i].val) < 0) { - pr_err("OV7670 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(200); + i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init)); + if (gspca_dev->usb_err < 0) + pr_err("OV7670 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 0; sd->vstart = 1; - return 0; } -static int mt9v_init_sensor(struct gspca_dev *gspca_dev) +static void mt9v_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; u16 value; - int ret; sd->i2c_addr = 0x5d; - ret = i2c_r2(gspca_dev, 0xff, &value); - if ((ret == 0) && (value == 0x8243)) { - for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { - if (i2c_w2(gspca_dev, mt9v011_init[i].reg, - mt9v011_init[i].val) < 0) { - pr_err("MT9V011 sensor initialization failed\n"); - return -ENODEV; - } + i2c_r2(gspca_dev, 0xff, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x8243) { + i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V011 sensor initialization failed\n"); + return; } sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V011; pr_info("MT9V011 sensor detected\n"); - return 0; + return; } + gspca_dev->usb_err = 0; sd->i2c_addr = 0x5c; i2c_w2(gspca_dev, 0x01, 0x0004); - ret = i2c_r2(gspca_dev, 0xff, &value); - if ((ret == 0) && (value == 0x823a)) { - for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { - if (i2c_w2(gspca_dev, mt9v111_init[i].reg, - mt9v111_init[i].val) < 0) { - pr_err("MT9V111 sensor initialization failed\n"); - return -ENODEV; - } + i2c_r2(gspca_dev, 0xff, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x823a) { + i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V111 sensor initialization failed\n"); + return; } - gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) - | (1 << AUTOGAIN_IDX) - | (1 << GAIN_IDX); + gspca_dev->ctrl_dis = (1 << EXPOSURE) + | (1 << AUTOGAIN) + | (1 << GAIN); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; pr_info("MT9V111 sensor detected\n"); - return 0; + return; } + gspca_dev->usb_err = 0; sd->i2c_addr = 0x5d; - ret = i2c_w2(gspca_dev, 0xf0, 0x0000); - if (ret < 0) { + i2c_w2(gspca_dev, 0xf0, 0x0000); + if (gspca_dev->usb_err < 0) { + gspca_dev->usb_err = 0; sd->i2c_addr = 0x48; i2c_w2(gspca_dev, 0xf0, 0x0000); } - ret = i2c_r2(gspca_dev, 0x00, &value); - if ((ret == 0) && (value == 0x1229)) { - for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { - if (i2c_w2(gspca_dev, mt9v112_init[i].reg, - mt9v112_init[i].val) < 0) { - pr_err("MT9V112 sensor initialization failed\n"); - return -ENODEV; - } + i2c_r2(gspca_dev, 0x00, &value); + if (gspca_dev->usb_err >= 0 + && value == 0x1229) { + i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init)); + if (gspca_dev->usb_err < 0) { + pr_err("MT9V112 sensor initialization failed\n"); + return; } sd->hstart = 6; sd->vstart = 2; sd->sensor = SENSOR_MT9V112; pr_info("MT9V112 sensor detected\n"); - return 0; + return; } - return -ENODEV; + gspca_dev->usb_err = -ENODEV; } -static int mt9m112_init_sensor(struct gspca_dev *gspca_dev) +static void 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) { - pr_err("MT9M112 sensor initialization failed\n"); - return -ENODEV; - } - } - gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) - | (1 << GAIN_IDX); + + i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M112 sensor initialization failed\n"); + + gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) + | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; - return 0; } -static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) +static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; - for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { - if (i2c_w2(gspca_dev, mt9m111_init[i].reg, - mt9m111_init[i].val) < 0) { - pr_err("MT9M111 sensor initialization failed\n"); - return -ENODEV; - } - } - gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) - | (1 << GAIN_IDX); + + i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M111 sensor initialization failed\n"); + + gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) + | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; - return 0; } -static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) +static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i; u16 id; - if (i2c_r2(gspca_dev, 0x00, &id) < 0) - return -EINVAL; + i2c_r2(gspca_dev, 0x00, &id); + if (gspca_dev->usb_err < 0) + return; /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ switch (id) { @@ -1494,85 +1462,78 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) break; default: pr_err("No MT9M001 chip detected, ID = %x\n\n", id); - return -ENODEV; + gspca_dev->usb_err = -ENODEV; + return; } - for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { - if (i2c_w2(gspca_dev, mt9m001_init[i].reg, - mt9m001_init[i].val) < 0) { - pr_err("MT9M001 sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init)); + if (gspca_dev->usb_err < 0) + pr_err("MT9M001 sensor initialization failed\n"); + /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 1; - return 0; } -static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) +static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) { - int i; struct sd *sd = (struct sd *) gspca_dev; - for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { - if (i2c_w1(gspca_dev, hv7131r_init[i].reg, - hv7131r_init[i].val) < 0) { - pr_err("HV7131R Sensor initialization failed\n"); - return -ENODEV; - } - } + i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init)); + if (gspca_dev->usb_err < 0) + pr_err("HV7131R Sensor initialization failed\n"); + sd->hstart = 0; sd->vstart = 1; - return 0; } -static int set_cmatrix(struct gspca_dev *gspca_dev) +static void set_cmatrix(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s32 hue_coord, hue_index = 180 + sd->hue; + int satur; + s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val; u8 cmatrix[21]; memset(cmatrix, 0, sizeof cmatrix); - cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26; + cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; - cmatrix[18] = sd->brightness - 0x80; + cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80; - hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8; + satur = sd->ctrls[SATURATION].val; + hue_coord = (hsv_red_x[hue_index] * satur) >> 8; cmatrix[6] = hue_coord; cmatrix[7] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_red_y[hue_index] * satur) >> 8; cmatrix[8] = hue_coord; cmatrix[9] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_green_x[hue_index] * satur) >> 8; cmatrix[10] = hue_coord; cmatrix[11] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_green_y[hue_index] * satur) >> 8; cmatrix[12] = hue_coord; cmatrix[13] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_blue_x[hue_index] * satur) >> 8; cmatrix[14] = hue_coord; cmatrix[15] = (hue_coord >> 8) & 0x0f; - hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8; + hue_coord = (hsv_blue_y[hue_index] * satur) >> 8; cmatrix[16] = hue_coord; cmatrix[17] = (hue_coord >> 8) & 0x0f; - return reg_w(gspca_dev, 0x10e1, cmatrix, 21); + reg_w(gspca_dev, 0x10e1, cmatrix, 21); } -static int set_gamma(struct gspca_dev *gspca_dev) +static void set_gamma(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 gamma[17]; - u8 gval = sd->gamma * 0xb8 / 0x100; - + u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100; gamma[0] = 0x0a; gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); @@ -1592,29 +1553,29 @@ static int set_gamma(struct gspca_dev *gspca_dev) gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8); gamma[16] = 0xf5; - return reg_w(gspca_dev, 0x1190, gamma, 17); + reg_w(gspca_dev, 0x1190, gamma, 17); } -static int set_redblue(struct gspca_dev *gspca_dev) +static void set_redblue(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w1(gspca_dev, 0x118c, sd->red); - reg_w1(gspca_dev, 0x118f, sd->blue); - return 0; + + reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val); + reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val); } -static int set_hvflip(struct gspca_dev *gspca_dev) +static void set_hvflip(struct gspca_dev *gspca_dev) { 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; + hflip = !sd->ctrls[HFLIP].val; + vflip = !sd->ctrls[VFLIP].val; } else { - hflip = sd->hflip; - vflip = sd->vflip; + hflip = sd->ctrls[HFLIP].val; + vflip = sd->ctrls[VFLIP].val; } switch (sd->sensor) { @@ -1625,8 +1586,9 @@ static int set_hvflip(struct gspca_dev *gspca_dev) if (vflip) { value |= 0x10; sd->vstart = 2; - } else + } else { sd->vstart = 3; + } reg_w1(gspca_dev, 0x1182, sd->vstart); i2c_w1(gspca_dev, 0x1e, value); break; @@ -1674,13 +1636,15 @@ static int set_hvflip(struct gspca_dev *gspca_dev) i2c_w1(gspca_dev, 0x01, value); break; } - return 0; } -static int set_exposure(struct gspca_dev *gspca_dev) +static void set_exposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; + int expo; + + expo = sd->ctrls[EXPOSURE].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1688,35 +1652,37 @@ static int set_exposure(struct gspca_dev *gspca_dev) case SENSOR_OV9650: exp[0] |= (3 << 4); exp[2] = 0x2d; - exp[3] = sd->exposure & 0xff; - exp[4] = sd->exposure >> 8; + exp[3] = expo; + exp[4] = expo >> 8; break; case SENSOR_MT9M001: case SENSOR_MT9V112: case SENSOR_MT9V011: exp[0] |= (3 << 4); exp[2] = 0x09; - exp[3] = sd->exposure >> 8; - exp[4] = sd->exposure & 0xff; + exp[3] = expo >> 8; + exp[4] = expo; break; case SENSOR_HV7131R: exp[0] |= (4 << 4); exp[2] = 0x25; - exp[3] = (sd->exposure >> 5) & 0xff; - exp[4] = (sd->exposure << 3) & 0xff; + exp[3] = expo >> 5; + exp[4] = expo << 3; exp[5] = 0; break; default: - return 0; + return; } i2c_w(gspca_dev, exp); - return 0; } -static int set_gain(struct gspca_dev *gspca_dev) +static void set_gain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; + int g; + + g = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1724,238 +1690,50 @@ static int set_gain(struct gspca_dev *gspca_dev) case SENSOR_OV9655: case SENSOR_OV9650: gain[0] |= (2 << 4); - gain[3] = ov_gain[sd->gain]; + gain[3] = ov_gain[g]; break; case SENSOR_MT9V011: gain[0] |= (3 << 4); gain[2] = 0x35; - gain[3] = micron1_gain[sd->gain] >> 8; - gain[4] = micron1_gain[sd->gain] & 0xff; + gain[3] = micron1_gain[g] >> 8; + gain[4] = micron1_gain[g]; break; case SENSOR_MT9V112: gain[0] |= (3 << 4); gain[2] = 0x2f; - gain[3] = micron1_gain[sd->gain] >> 8; - gain[4] = micron1_gain[sd->gain] & 0xff; + gain[3] = micron1_gain[g] >> 8; + gain[4] = micron1_gain[g]; break; case SENSOR_MT9M001: gain[0] |= (3 << 4); gain[2] = 0x2f; - gain[3] = micron2_gain[sd->gain] >> 8; - gain[4] = micron2_gain[sd->gain] & 0xff; + gain[3] = micron2_gain[g] >> 8; + gain[4] = micron2_gain[g]; break; case SENSOR_HV7131R: gain[0] |= (2 << 4); gain[2] = 0x30; - gain[3] = hv7131r_gain[sd->gain]; + gain[3] = hv7131r_gain[g]; break; default: - return 0; + return; } i2c_w(gspca_dev, gain); - return 0; -} - -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) - return set_cmatrix(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; - - sd->contrast = val; - if (gspca_dev->streaming) - return set_cmatrix(gspca_dev); - return 0; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->contrast; - return 0; -} - -static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->saturation = val; - if (gspca_dev->streaming) - return set_cmatrix(gspca_dev); - return 0; } -static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val) +static void set_quality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->saturation; - 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) - return set_cmatrix(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_setgamma(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gamma = val; - if (gspca_dev->streaming) - return set_gamma(gspca_dev); - return 0; -} - -static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->gamma; - return 0; -} - -static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->red = val; - if (gspca_dev->streaming) - return set_redblue(gspca_dev); - return 0; -} - -static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->red; - return 0; -} - -static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->blue = val; - if (gspca_dev->streaming) - return set_redblue(gspca_dev); - return 0; -} - -static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->blue; - return 0; -} - -static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hflip = val; - if (gspca_dev->streaming) - return set_hvflip(gspca_dev); - return 0; -} - -static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->hflip; - return 0; -} - -static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vflip = val; - if (gspca_dev->streaming) - return set_hvflip(gspca_dev); - return 0; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->vflip; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - return set_exposure(gspca_dev); - return 0; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->exposure; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - return set_gain(gspca_dev); - return 0; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->gain; - return 0; -} - -static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - sd->auto_exposure = val; - return 0; -} -static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - *val = sd->auto_exposure; - return 0; + jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ + reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ + reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); + reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */ + reg_w1(gspca_dev, 0x10e0, sd->fmt); + sd->fmt ^= 0x0c; /* invert QTAB use + write */ + reg_w1(gspca_dev, 0x10e0, sd->fmt); } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1963,28 +1741,26 @@ static int sd_dbg_g_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { case V4L2_CHIP_MATCH_HOST: if (reg->match.addr != 0) return -EINVAL; if (reg->reg < 0x1000 || reg->reg > 0x11ff) return -EINVAL; - if (reg_r(gspca_dev, reg->reg, 1) < 0) - return -EINVAL; + reg_r(gspca_dev, reg->reg, 1); reg->val = gspca_dev->usb_buf[0]; - return 0; + return gspca_dev->usb_err; case V4L2_CHIP_MATCH_I2C_ADDR: if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && sd->sensor <= SENSOR_MT9M112) { - if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) - return -EINVAL; + i2c_r2(gspca_dev, reg->reg, (u16 *) ®->val); } else { - if (i2c_r1(gspca_dev, reg->reg, (u8 *)®->val) < 0) - return -EINVAL; + i2c_r1(gspca_dev, reg->reg, (u8 *) ®->val); } - return 0; + return gspca_dev->usb_err; } return -EINVAL; } @@ -1993,27 +1769,25 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { struct sd *sd = (struct sd *) gspca_dev; + switch (reg->match.type) { case V4L2_CHIP_MATCH_HOST: if (reg->match.addr != 0) return -EINVAL; if (reg->reg < 0x1000 || reg->reg > 0x11ff) return -EINVAL; - if (reg_w1(gspca_dev, reg->reg, reg->val) < 0) - return -EINVAL; - return 0; + reg_w1(gspca_dev, reg->reg, reg->val); + return gspca_dev->usb_err; case V4L2_CHIP_MATCH_I2C_ADDR: if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && sd->sensor <= SENSOR_MT9M112) { - if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) - return -EINVAL; + i2c_w2(gspca_dev, reg->reg, reg->val); } else { - if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0) - return -EINVAL; + i2c_w1(gspca_dev, reg->reg, reg->val); } - return 0; + return gspca_dev->usb_err; } return -EINVAL; } @@ -2050,9 +1824,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->needs_full_bandwidth = 1; - sd->sensor = (id->driver_info >> 8) & 0xff; - sd->i2c_addr = id->driver_info & 0xff; - sd->flags = (id->driver_info >> 16) & 0xff; + sd->sensor = id->driver_info >> 8; + sd->i2c_addr = id->driver_info; + sd->flags = id->driver_info >> 16; switch (sd->sensor) { case SENSOR_MT9M112: @@ -2076,21 +1850,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->older_step = 0; sd->exposure_step = 16; - sd->brightness = BRIGHTNESS_DEFAULT; - sd->contrast = CONTRAST_DEFAULT; - sd->saturation = SATURATION_DEFAULT; - sd->hue = HUE_DEFAULT; - sd->gamma = GAMMA_DEFAULT; - sd->red = RED_DEFAULT; - sd->blue = BLUE_DEFAULT; + gspca_dev->cam.ctrls = sd->ctrls; - sd->hflip = HFLIP_DEFAULT; - sd->vflip = VFLIP_DEFAULT; - sd->exposure = EXPOSURE_DEFAULT; - sd->gain = GAIN_DEFAULT; - sd->auto_exposure = AUTO_EXPOSURE_DEFAULT; - - sd->quality = 95; + INIT_WORK(&sd->work, qual_upd); return 0; } @@ -2105,9 +1867,10 @@ static int sd_init(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { value = bridge_init[i][1]; - if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) { + reg_w(gspca_dev, bridge_init[i][0], &value, 1); + if (gspca_dev->usb_err < 0) { pr_err("Device initialization failed\n"); - return -ENODEV; + return gspca_dev->usb_err; } } @@ -2116,72 +1879,85 @@ static int sd_init(struct gspca_dev *gspca_dev) else reg_w1(gspca_dev, 0x1006, 0x20); - if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { + reg_w(gspca_dev, 0x10c0, i2c_init, 9); + if (gspca_dev->usb_err < 0) { pr_err("Device initialization failed\n"); - return -ENODEV; + return gspca_dev->usb_err; } switch (sd->sensor) { case SENSOR_OV9650: - if (ov9650_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov9650_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV9650 sensor detected\n"); break; case SENSOR_OV9655: - if (ov9655_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov9655_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV9655 sensor detected\n"); break; case SENSOR_SOI968: - if (soi968_init_sensor(gspca_dev) < 0) - return -ENODEV; + soi968_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("SOI968 sensor detected\n"); break; case SENSOR_OV7660: - if (ov7660_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov7660_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV7660 sensor detected\n"); break; case SENSOR_OV7670: - if (ov7670_init_sensor(gspca_dev) < 0) - return -ENODEV; + ov7670_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("OV7670 sensor detected\n"); break; case SENSOR_MT9VPRB: - if (mt9v_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9v_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; + pr_info("MT9VPRB sensor detected\n"); break; case SENSOR_MT9M111: - if (mt9m111_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9m111_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("MT9M111 sensor detected\n"); break; case SENSOR_MT9M112: - if (mt9m112_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9m112_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("MT9M112 sensor detected\n"); break; case SENSOR_MT9M001: - if (mt9m001_init_sensor(gspca_dev) < 0) - return -ENODEV; + mt9m001_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; break; case SENSOR_HV7131R: - if (hv7131r_init_sensor(gspca_dev) < 0) - return -ENODEV; + hv7131r_init_sensor(gspca_dev); + if (gspca_dev->usb_err < 0) + break; pr_info("HV7131R sensor detected\n"); break; default: - pr_info("Unsupported Sensor\n"); - return -ENODEV; + pr_err("Unsupported sensor\n"); + gspca_dev->usb_err = -ENODEV; } - return 0; + return gspca_dev->usb_err; } static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) { struct sd *sd = (struct sd *) gspca_dev; u8 value; + switch (sd->sensor) { case SENSOR_SOI968: if (mode & MODE_SXGA) { @@ -2264,6 +2040,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) break; default: /* >= 640x480 */ gspca_dev->alt = 9; + break; } } @@ -2290,14 +2067,15 @@ static int sd_start(struct gspca_dev *gspca_dev) jpeg_define(sd->jpeg_hdr, height, width, 0x21); - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); if (mode & MODE_RAW) fmt = 0x2d; else if (mode & MODE_JPEG) - fmt = 0x2c; + fmt = 0x24; else fmt = 0x2f; /* YUV 420 */ + sd->fmt = fmt; switch (mode & SCALE_MASK) { case SCALE_1280x1024: @@ -2334,18 +2112,37 @@ static int sd_start(struct gspca_dev *gspca_dev) set_hvflip(gspca_dev); reg_w1(gspca_dev, 0x1007, 0x20); + reg_w1(gspca_dev, 0x1061, 0x03); + + /* if JPEG, prepare the compression quality update */ + if (mode & MODE_JPEG) { + sd->pktsz = sd->npkt = 0; + sd->nchg = 0; + sd->work_thread = + create_singlethread_workqueue(KBUILD_MODNAME); + } - reg_r(gspca_dev, 0x1061, 1); - reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) { reg_w1(gspca_dev, 0x1007, 0x00); + reg_w1(gspca_dev, 0x1061, 0x01); +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; - reg_r(gspca_dev, 0x1061, 1); - reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } } static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) @@ -2359,15 +2156,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) * and exposure steps */ if (avg_lum < MIN_AVG_LUM) { - if (sd->exposure > 0x1770) + if (sd->ctrls[EXPOSURE].val > 0x1770) return; - new_exp = sd->exposure + sd->exposure_step; + new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step; if (new_exp > 0x1770) new_exp = 0x1770; if (new_exp < 0x10) new_exp = 0x10; - sd->exposure = new_exp; + sd->ctrls[EXPOSURE].val = new_exp; set_exposure(gspca_dev); sd->older_step = sd->old_step; @@ -2379,14 +2176,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) sd->exposure_step += 2; } if (avg_lum > MAX_AVG_LUM) { - if (sd->exposure < 0x10) + if (sd->ctrls[EXPOSURE].val < 0x10) return; - new_exp = sd->exposure - sd->exposure_step; + new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step; if (new_exp > 0x1700) new_exp = 0x1770; if (new_exp < 0x10) new_exp = 0x10; - sd->exposure = new_exp; + sd->ctrls[EXPOSURE].val = new_exp; set_exposure(gspca_dev); sd->older_step = sd->old_step; sd->old_step = 0; @@ -2403,14 +2200,14 @@ static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) struct sd *sd = (struct sd *) gspca_dev; if (avg_lum < MIN_AVG_LUM) { - if (sd->gain + 1 <= 28) { - sd->gain++; + if (sd->ctrls[GAIN].val + 1 <= 28) { + sd->ctrls[GAIN].val++; set_gain(gspca_dev); } } if (avg_lum > MAX_AVG_LUM) { - if (sd->gain > 0) { - sd->gain--; + if (sd->ctrls[GAIN].val > 0) { + sd->ctrls[GAIN].val--; set_gain(gspca_dev); } } @@ -2421,7 +2218,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum; - if (!sd->auto_exposure) + if (!sd->ctrls[AUTOGAIN].val) return; avg_lum = atomic_read(&sd->avg_lum); @@ -2431,33 +2228,92 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) do_autoexposure(gspca_dev, avg_lum); } +/* JPEG quality update */ +/* This function is executed from a work queue. */ +static void qual_upd(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + mutex_lock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); + set_quality(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); +} + #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 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; + 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); + return 0; } - return ret; + return -EINVAL; } #endif +/* check the JPEG compression */ +static void transfer_check(struct gspca_dev *gspca_dev, + u8 *data) +{ + struct sd *sd = (struct sd *) gspca_dev; + int new_qual, r; + + new_qual = 0; + + /* if USB error, discard the frame and decrease the quality */ + if (data[6] & 0x08) { /* USB FIFO full */ + gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -5; + } else { + + /* else, compute the filling rate and a new JPEG quality */ + r = (sd->pktsz * 100) / + (sd->npkt * + gspca_dev->urb[0]->iso_frame_desc[0].length); + if (r >= 85) + new_qual = -3; + else if (r < 75) + new_qual = 2; + } + if (new_qual != 0) { + sd->nchg += new_qual; + if (sd->nchg < -6 || sd->nchg >= 12) { + sd->nchg = 0; + new_qual += sd->ctrls[QUALITY].val; + if (new_qual < QUALITY_MIN) + new_qual = QUALITY_MIN; + else if (new_qual > QUALITY_MAX) + new_qual = QUALITY_MAX; + if (new_qual != sd->ctrls[QUALITY].val) { + sd->ctrls[QUALITY].val = new_qual; + queue_work(sd->work_thread, &sd->work); + } + } + } else { + sd->nchg = 0; + } + sd->pktsz = sd->npkt = 0; +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - int avg_lum; + int avg_lum, is_jpeg; static u8 frame_header[] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - if (len == 64 && memcmp(data, frame_header, 6) == 0) { + + is_jpeg = (sd->fmt & 0x03) == 0; + if (len >= 64 && memcmp(data, frame_header, 6) == 0) { avg_lum = ((data[35] >> 2) & 3) | (data[20] << 2) | (data[19] << 10); @@ -2484,12 +2340,18 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, (data[33] << 10); avg_lum >>= 9; atomic_set(&sd->avg_lum, avg_lum); + + if (is_jpeg) + transfer_check(gspca_dev, data); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); - return; + len -= 64; + if (len == 0) + return; + data += 64; } if (gspca_dev->last_packet_type == LAST_PACKET) { - if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv - & MODE_JPEG) { + if (is_jpeg) { gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); gspca_frame_add(gspca_dev, INTER_PACKET, @@ -2499,13 +2361,18 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); } } else { + /* if JPEG, count the packets and their size */ + if (is_jpeg) { + sd->npkt++; + sd->pktsz += len; + } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } } /* sub-driver description */ static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, @@ -2513,6 +2380,7 @@ static const struct sd_desc sd_desc = { .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .int_pkt_scan = sd_int_pkt_scan, @@ -2581,7 +2449,7 @@ static int sd_probe(struct usb_interface *intf, } static struct usb_driver sd_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 0c9e6dd..db8e508 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -39,7 +39,9 @@ enum e_ctrl { BLUE, RED, GAMMA, + EXPOSURE, AUTOGAIN, + GAIN, HFLIP, VFLIP, SHARPNESS, @@ -131,7 +133,9 @@ static void setcontrast(struct gspca_dev *gspca_dev); static void setcolors(struct gspca_dev *gspca_dev); static void setredblue(struct gspca_dev *gspca_dev); static void setgamma(struct gspca_dev *gspca_dev); -static void setautogain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static void setgain(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); static void setillum(struct gspca_dev *gspca_dev); @@ -213,6 +217,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = { }, .set_control = setgamma }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 500, + .maximum = 1500, + .step = 1, + .default_value = 1024 + }, + .set_control = setexposure + }, [AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, @@ -223,7 +239,19 @@ static const struct ctrl sd_ctrls[NCTRLS] = { .step = 1, .default_value = 1 }, - .set_control = setautogain + .set = sd_setautogain, + }, +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 4, + .maximum = 49, + .step = 1, + .default_value = 15 + }, + .set_control = setgain }, [HFLIP] = { { @@ -290,60 +318,87 @@ static const struct ctrl sd_ctrls[NCTRLS] = { /* table of the disabled controls */ static const __u32 ctrl_dis[] = { -[SENSOR_ADCM1700] = (1 << AUTOGAIN) | +[SENSOR_ADCM1700] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_GC0307] = (1 << HFLIP) | +[SENSOR_GC0307] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_HV7131R] = (1 << HFLIP) | +[SENSOR_HV7131R] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << FREQ), -[SENSOR_MI0360] = (1 << HFLIP) | +[SENSOR_MI0360] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MI0360B] = (1 << HFLIP) | +[SENSOR_MI0360B] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MO4000] = (1 << HFLIP) | +[SENSOR_MO4000] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MT9V111] = (1 << HFLIP) | +[SENSOR_MT9V111] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_OM6802] = (1 << HFLIP) | +[SENSOR_OM6802] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_OV7630] = (1 << HFLIP), +[SENSOR_OV7630] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP), -[SENSOR_OV7648] = (1 << HFLIP), +[SENSOR_OV7648] = (1 << EXPOSURE) | + (1 << GAIN) | + (1 << HFLIP), -[SENSOR_OV7660] = (1 << AUTOGAIN) | +[SENSOR_OV7660] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP), -[SENSOR_PO1030] = (1 << AUTOGAIN) | +[SENSOR_PO1030] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_PO2030N] = (1 << AUTOGAIN) | - (1 << FREQ), +[SENSOR_PO2030N] = (1 << FREQ), -[SENSOR_SOI768] = (1 << AUTOGAIN) | +[SENSOR_SOI768] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_SP80708] = (1 << AUTOGAIN) | +[SENSOR_SP80708] = (1 << EXPOSURE) | + (1 << AUTOGAIN) | + (1 << GAIN) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), @@ -1242,14 +1297,6 @@ static const u8 po2030n_sensor_param1[][8] = { {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}, - {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ - {0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10}, - {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ - {0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10}, {} }; @@ -1858,7 +1905,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } -static u32 setexposure(struct gspca_dev *gspca_dev, +static u32 expo_adjust(struct gspca_dev *gspca_dev, u32 expo) { struct sd *sd = (struct sd *) gspca_dev; @@ -1982,28 +2029,28 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = 0x002dc6c0; else if (expo < 0x02a0) expo = 0x02a0; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); break; case SENSOR_MI0360: case SENSOR_MO4000: expo = brightness << 4; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); break; case SENSOR_MI0360B: expo = brightness << 2; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); break; case SENSOR_GC0307: expo = brightness; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); return; /* don't set the Y offset */ case SENSOR_MT9V111: expo = brightness << 2; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); return; /* don't set the Y offset */ case SENSOR_OM6802: expo = brightness << 2; - sd->exposure = setexposure(gspca_dev, expo); + sd->exposure = expo_adjust(gspca_dev, expo); return; /* Y offset already set */ } @@ -2112,6 +2159,23 @@ static void setgamma(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x20, gamma, sizeof gamma); } +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rexpo[] = /* 1a: expo H, 1b: expo M */ + {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10}; + + rexpo[3] = sd->ctrls[EXPOSURE].val >> 8; + i2c_w8(gspca_dev, rexpo); + msleep(6); + rexpo[2] = 0x1b; + rexpo[3] = sd->ctrls[EXPOSURE].val; + i2c_w8(gspca_dev, rexpo); + } +} + static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2139,6 +2203,19 @@ static void setautogain(struct gspca_dev *gspca_dev) sd->ag_cnt = -1; } +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PO2030N) { + u8 rgain[] = /* 15: gain */ + {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15}; + + rgain[3] = sd->ctrls[GAIN].val; + i2c_w8(gspca_dev, rgain); + } +} + static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2623,6 +2700,10 @@ static int sd_start(struct gspca_dev *gspca_dev) setcontrast(gspca_dev); setcolors(gspca_dev); setautogain(gspca_dev); + if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) { + setexposure(gspca_dev); + setgain(gspca_dev); + } setfreq(gspca_dev); sd->pktsz = sd->npkt = 0; @@ -2719,6 +2800,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } } +/* !! coarse_grained_expo_autogain is not used !! */ +#define exp_too_low_cnt bridge +#define exp_too_high_cnt sensor + +#include "autogain_functions.h" + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2736,6 +2823,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) delta = atomic_read(&sd->avg_lum); PDEBUG(D_FRAM, "mean lum %d", delta); + + if (sd->sensor == SENSOR_PO2030N) { + auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta, + 15, 1024); + return; + } + if (delta < luma_mean - luma_delta || delta > luma_mean + luma_delta) { switch (sd->sensor) { @@ -2744,7 +2838,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) expotimes); break; case SENSOR_HV7131R: @@ -2752,7 +2846,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 4; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) (expotimes << 8)); break; case SENSOR_OM6802: @@ -2761,7 +2855,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 2; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) expotimes); setredblue(gspca_dev); break; @@ -2773,7 +2867,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) expotimes = 0; - sd->exposure = setexposure(gspca_dev, + sd->exposure = expo_adjust(gspca_dev, (unsigned int) expotimes); setredblue(gspca_dev); break; @@ -2948,16 +3042,18 @@ marker_found: } } -static int sd_get_jcomp(struct gspca_dev *gspca_dev, - struct v4l2_jpegcompression *jcomp) +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; - jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT - | V4L2_JPEG_MARKER_DQT; - return 0; + sd->ctrls[AUTOGAIN].val = val; + if (val) + gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); + else + gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN); + if (gspca_dev->streaming) + setautogain(gspca_dev); + return gspca_dev->usb_err; } static int sd_querymenu(struct gspca_dev *gspca_dev, @@ -3012,7 +3108,6 @@ static const struct sd_desc sd_desc = { .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, - .get_jcomp = sd_get_jcomp, .querymenu = sd_querymenu, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .int_pkt_scan = sd_int_pkt_scan, diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile index 5b318fa..38bc410 100644 --- a/drivers/media/video/gspca/stv06xx/Makefile +++ b/drivers/media/video/gspca/stv06xx/Makefile @@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \ stv06xx_pb0100.o \ stv06xx_st6422.o -ccflags-y += -Idrivers/media/video/gspca +ccflags-y += -I$(srctree)/drivers/media/video/gspca diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index b9e15bb..7d9a4f1 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -1,7 +1,7 @@ /* - * Z-Star/Vimicro zc301/zc302p/vc30x library + * Z-Star/Vimicro zc301/zc302p/vc30x driver * - * Copyright (C) 2009-2011 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr> * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify @@ -21,8 +21,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define MODULE_NAME "zc3xx" - #include <linux/input.h> #include "gspca.h" #include "jpeg.h" @@ -34,7 +32,7 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; -#define QUANT_VAL 1 /* quantization table */ +#define REG08_DEF 3 /* default JPEG compression (70%) */ #include "zc3xx-reg.h" /* controls */ @@ -46,6 +44,7 @@ enum e_ctrl { AUTOGAIN, LIGHTFREQ, SHARPNESS, + QUALITY, NCTRLS /* number of controls */ }; @@ -57,10 +56,10 @@ struct sd { struct gspca_ctrl ctrls[NCTRLS]; - u8 quality; /* image quality */ -#define QUALITY_MIN 50 -#define QUALITY_MAX 80 -#define QUALITY_DEF 70 + struct work_struct work; + struct workqueue_struct *work_thread; + + u8 reg08; /* webcam compression quality */ u8 bridge; u8 sensor; /* Type of image sensor chip */ @@ -101,6 +100,7 @@ static void setexposure(struct gspca_dev *gspca_dev); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static void setlightfreq(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); +static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val); static const struct ctrl sd_ctrls[NCTRLS] = { [BRIGHTNESS] = { @@ -188,6 +188,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = { }, .set_control = setsharpness }, +[QUALITY] = { + { + .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Compression Quality", + .minimum = 40, + .maximum = 70, + .step = 1, + .default_value = 70 /* updated in sd_init() */ + }, + .set = sd_setquality + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -229,6 +241,9 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; +/* bridge reg08 -> JPEG quality conversion table */ +static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/}; + /* usb exchanges */ struct usb_action { u8 req; @@ -3894,7 +3909,6 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */ /* Gains */ {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* Auto correction */ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, @@ -5640,7 +5654,7 @@ static const struct usb_action gc0303_NoFlikerScale[] = { {} }; -static u8 reg_r_i(struct gspca_dev *gspca_dev, +static u8 reg_r(struct gspca_dev *gspca_dev, u16 index) { int ret; @@ -5655,24 +5669,14 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - pr_err("reg_r_i err %d\n", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; return 0; } return gspca_dev->usb_buf[0]; } -static u8 reg_r(struct gspca_dev *gspca_dev, - u16 index) -{ - u8 ret; - - ret = reg_r_i(gspca_dev, index); - PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret); - return ret; -} - -static void reg_w_i(struct gspca_dev *gspca_dev, +static void reg_w(struct gspca_dev *gspca_dev, u8 value, u16 index) { @@ -5692,14 +5696,6 @@ static void reg_w_i(struct gspca_dev *gspca_dev, } } -static void reg_w(struct gspca_dev *gspca_dev, - u8 value, - u16 index) -{ - PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value); - reg_w_i(gspca_dev, value, index); -} - static u16 i2c_read(struct gspca_dev *gspca_dev, u8 reg) { @@ -5708,16 +5704,14 @@ static u16 i2c_read(struct gspca_dev *gspca_dev, if (gspca_dev->usb_err < 0) return 0; - reg_w_i(gspca_dev, reg, 0x0092); - reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */ + reg_w(gspca_dev, reg, 0x0092); + reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */ msleep(20); - retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + retbyte = reg_r(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) pr_err("i2c_r status error %02x\n", retbyte); - retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ - retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ - PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", - reg, retval, retbyte); + retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */ + retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */ return retval; } @@ -5730,16 +5724,14 @@ static u8 i2c_write(struct gspca_dev *gspca_dev, if (gspca_dev->usb_err < 0) return 0; - reg_w_i(gspca_dev, reg, 0x92); - reg_w_i(gspca_dev, valL, 0x93); - reg_w_i(gspca_dev, valH, 0x94); - reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */ + reg_w(gspca_dev, reg, 0x92); + reg_w(gspca_dev, valL, 0x93); + reg_w(gspca_dev, valH, 0x94); + reg_w(gspca_dev, 0x01, 0x90); /* <- write command */ msleep(1); - retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + retbyte = reg_r(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) pr_err("i2c_w status error %02x\n", retbyte); - PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", - reg, valH, valL, retbyte); return retbyte; } @@ -5906,6 +5898,8 @@ static void getexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (sd->sensor != SENSOR_HV7131R) + return; sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9) | (i2c_read(gspca_dev, 0x26) << 1) | (i2c_read(gspca_dev, 0x27) >> 7); @@ -5916,6 +5910,8 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int val; + if (sd->sensor != SENSOR_HV7131R) + return; val = sd->ctrls[EXPOSURE].val; i2c_write(gspca_dev, 0x25, val >> 9, 0x00); i2c_write(gspca_dev, 0x26, val >> 1, 0x00); @@ -5925,32 +5921,20 @@ static void setexposure(struct gspca_dev *gspca_dev) static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 frxt; + s8 reg07; + reg07 = 0; switch (sd->sensor) { - case SENSOR_ADCM2700: - case SENSOR_GC0305: - case SENSOR_HV7131B: - case SENSOR_HV7131R: case SENSOR_OV7620: + reg07 = 0x30; + break; + case SENSOR_HV7131R: case SENSOR_PAS202B: - case SENSOR_PO2030: - return; + return; /* done by work queue */ } -/*fixme: is it really 0008 0007 0018 for all other sensors? */ - reg_w(gspca_dev, QUANT_VAL, 0x0008); - frxt = 0x30; - reg_w(gspca_dev, frxt, 0x0007); -#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2 - frxt = 0xff; -#elif QUANT_VAL == 3 - frxt = 0xf0; -#elif QUANT_VAL == 4 - frxt = 0xe0; -#else - frxt = 0x20; -#endif - reg_w(gspca_dev, frxt, 0x0018); + reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); + if (reg07 != 0) + reg_w(gspca_dev, reg07, 0x0007); } /* Matches the sensor's internal frame rate to the lighting frequency. @@ -6084,6 +6068,115 @@ static void setautogain(struct gspca_dev *gspca_dev) reg_w(gspca_dev, autoval, 0x0180); } +/* update the transfer parameters */ +/* This function is executed from a work queue. */ +/* The exact use of the bridge registers 07 and 08 is not known. + * The following algorithm has been adapted from ms-win traces */ +static void transfer_update(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + int change, good; + u8 reg07, reg11; + + /* synchronize with the main driver and initialize the registers */ + mutex_lock(&gspca_dev->usb_lock); + reg07 = 0; /* max */ + reg_w(gspca_dev, reg07, 0x0007); + reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); + mutex_unlock(&gspca_dev->usb_lock); + + good = 0; + for (;;) { + msleep(100); + + /* get the transfer status */ + /* the bit 0 of the bridge register 11 indicates overflow */ + mutex_lock(&gspca_dev->usb_lock); + if (!gspca_dev->present || !gspca_dev->streaming) + goto err; + reg11 = reg_r(gspca_dev, 0x0011); + if (gspca_dev->usb_err < 0 + || !gspca_dev->present || !gspca_dev->streaming) + goto err; + + change = reg11 & 0x01; + if (change) { /* overflow */ + switch (reg07) { + case 0: /* max */ + reg07 = sd->sensor == SENSOR_HV7131R + ? 0x30 : 0x32; + if (sd->reg08 != 0) { + change = 3; + sd->reg08--; + } + break; + case 0x32: + reg07 -= 4; + break; + default: + reg07 -= 2; + break; + case 2: + change = 0; /* already min */ + break; + } + good = 0; + } else { /* no overflow */ + if (reg07 != 0) { /* if not max */ + good++; + if (good >= 10) { + good = 0; + change = 1; + reg07 += 2; + switch (reg07) { + case 0x30: + if (sd->sensor == SENSOR_PAS202B) + reg07 += 2; + break; + case 0x32: + case 0x34: + reg07 = 0; + break; + } + } + } else { /* reg07 max */ + if (sd->reg08 < sizeof jpeg_qual - 1) { + good++; + if (good > 10) { + sd->reg08++; + change = 2; + } + } + } + } + if (change) { + if (change & 1) { + reg_w(gspca_dev, reg07, 0x0007); + if (gspca_dev->usb_err < 0 + || !gspca_dev->present + || !gspca_dev->streaming) + goto err; + } + if (change & 2) { + reg_w(gspca_dev, sd->reg08, + ZC3XX_R008_CLOCKSETTING); + if (gspca_dev->usb_err < 0 + || !gspca_dev->present + || !gspca_dev->streaming) + goto err; + sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08]; + jpeg_set_qual(sd->jpeg_hdr, + jpeg_qual[sd->reg08]); + } + } + mutex_unlock(&gspca_dev->usb_lock); + } + return; +err: + mutex_unlock(&gspca_dev->usb_lock); +} + static void send_unknown(struct gspca_dev *gspca_dev, int sensor) { reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ @@ -6411,7 +6504,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info; gspca_dev->cam.ctrls = sd->ctrls; - sd->quality = QUALITY_DEF; + sd->reg08 = REG08_DEF; + + INIT_WORK(&sd->work, transfer_update); return 0; } @@ -6464,6 +6559,27 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 1, [SENSOR_TAS5130C] = 1, }; + static const u8 reg08_tb[SENSOR_MAX] = { + [SENSOR_ADCM2700] = 1, + [SENSOR_CS2102] = 3, + [SENSOR_CS2102K] = 3, + [SENSOR_GC0303] = 2, + [SENSOR_GC0305] = 3, + [SENSOR_HDCS2020] = 1, + [SENSOR_HV7131B] = 3, + [SENSOR_HV7131R] = 3, + [SENSOR_ICM105A] = 3, + [SENSOR_MC501CB] = 3, + [SENSOR_MT9V111_1] = 3, + [SENSOR_MT9V111_3] = 3, + [SENSOR_OV7620] = 1, + [SENSOR_OV7630C] = 3, + [SENSOR_PAS106] = 3, + [SENSOR_PAS202B] = 3, + [SENSOR_PB0330] = 3, + [SENSOR_PO2030] = 2, + [SENSOR_TAS5130C] = 3, + }; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) @@ -6528,7 +6644,6 @@ static int sd_init(struct gspca_dev *gspca_dev) case 0x0e: PDEBUG(D_PROBE, "Find Sensor PAS202B"); sd->sensor = SENSOR_PAS202B; -/* sd->sharpness = 1; */ break; case 0x0f: PDEBUG(D_PROBE, "Find Sensor PAS106"); @@ -6616,13 +6731,21 @@ static int sd_init(struct gspca_dev *gspca_dev) } sd->ctrls[GAMMA].def = gamma[sd->sensor]; + sd->reg08 = reg08_tb[sd->sensor]; + sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08]; + sd->ctrls[QUALITY].min = jpeg_qual[0]; + sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1]; switch (sd->sensor) { case SENSOR_HV7131R: + gspca_dev->ctrl_dis = (1 << QUALITY); break; case SENSOR_OV7630C: gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); break; + case SENSOR_PAS202B: + gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE); + break; default: gspca_dev->ctrl_dis = (1 << EXPOSURE); break; @@ -6685,7 +6808,6 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, sd->quality); mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->sensor) { @@ -6761,10 +6883,9 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_r(gspca_dev, 0x0180); /* from win */ reg_w(gspca_dev, 0x00, 0x0180); break; - default: - setquality(gspca_dev); - break; } + setquality(gspca_dev); + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); setlightfreq(gspca_dev); switch (sd->sensor) { @@ -6776,8 +6897,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_HV7131R: - if (!sd->ctrls[AUTOGAIN].val) - setexposure(gspca_dev); + setexposure(gspca_dev); reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); break; case SENSOR_GC0305: @@ -6802,13 +6922,19 @@ static int sd_start(struct gspca_dev *gspca_dev) } setautogain(gspca_dev); - switch (sd->sensor) { - case SENSOR_PO2030: - msleep(50); - reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */ - reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING); - break; + + /* start the transfer update thread if needed */ + if (gspca_dev->usb_err >= 0) { + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_PAS202B: + sd->work_thread = + create_singlethread_workqueue(KBUILD_MODNAME); + queue_work(sd->work_thread, &sd->work); + break; + } } + return gspca_dev->usb_err; } @@ -6817,6 +6943,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } if (!gspca_dev->present) return; send_unknown(gspca_dev, sd->sensor); @@ -6893,19 +7025,33 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) { + if (val <= jpeg_qual[i]) + break; + } + if (i > 0 + && i == sd->reg08 + && val < jpeg_qual[sd->reg08]) + i--; + sd->reg08 = i; + sd->ctrls[QUALITY].val = jpeg_qual[i]; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + return gspca_dev->usb_err; +} + static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; - if (jcomp->quality < QUALITY_MIN) - sd->quality = QUALITY_MIN; - else if (jcomp->quality > QUALITY_MAX) - sd->quality = QUALITY_MAX; - else - sd->quality = jcomp->quality; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + sd_setquality(gspca_dev, jcomp->quality); + jcomp->quality = sd->ctrls[QUALITY].val; return gspca_dev->usb_err; } @@ -6915,7 +7061,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; + jcomp->quality = sd->ctrls[QUALITY].val; jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -6938,7 +7084,7 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, #endif static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, @@ -7023,7 +7169,7 @@ static int sd_probe(struct usb_interface *intf, /* USB driver */ static struct usb_driver sd_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c index eec75bb..351e9ba 100644 --- a/drivers/media/video/imx074.c +++ b/drivers/media/video/imx074.c @@ -468,18 +468,7 @@ static struct i2c_driver imx074_i2c_driver = { .id_table = imx074_id, }; -static int __init imx074_mod_init(void) -{ - return i2c_add_driver(&imx074_i2c_driver); -} - -static void __exit imx074_mod_exit(void) -{ - i2c_del_driver(&imx074_i2c_driver); -} - -module_init(imx074_mod_init); -module_exit(imx074_mod_exit); +module_i2c_driver(imx074_i2c_driver); MODULE_DESCRIPTION("Sony IMX074 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c index e5ed4db..5482363 100644 --- a/drivers/media/video/indycam.c +++ b/drivers/media/video/indycam.c @@ -387,15 +387,4 @@ static struct i2c_driver indycam_driver = { .id_table = indycam_id, }; -static __init int init_indycam(void) -{ - return i2c_add_driver(&indycam_driver); -} - -static __exit void exit_indycam(void) -{ - i2c_del_driver(&indycam_driver); -} - -module_init(init_indycam); -module_exit(exit_indycam); +module_i2c_driver(indycam_driver); diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index a7c41d3..04f192a 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -471,7 +471,7 @@ static const struct i2c_device_id ir_kbd_id[] = { { } }; -static struct i2c_driver driver = { +static struct i2c_driver ir_kbd_driver = { .driver = { .name = "ir-kbd-i2c", }, @@ -480,21 +480,10 @@ static struct i2c_driver driver = { .id_table = ir_kbd_id, }; +module_i2c_driver(ir_kbd_driver); + /* ----------------------------------------------------------------------- */ MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); MODULE_DESCRIPTION("input driver for i2c IR remote controls"); MODULE_LICENSE("GPL"); - -static int __init ir_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit ir_fini(void) -{ - i2c_del_driver(&driver); -} - -module_init(ir_init); -module_exit(ir_fini); diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile index 71ab76a..77de8a4 100644 --- a/drivers/media/video/ivtv/Makefile +++ b/drivers/media/video/ivtv/Makefile @@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index b31ee1bc..c604246 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -21,6 +21,7 @@ #include "ivtv-driver.h" #include "ivtv-ioctl.h" #include "ivtv-controls.h" +#include "ivtv-mailbox.h" static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) { @@ -99,3 +100,64 @@ struct cx2341x_handler_ops ivtv_cxhdl_ops = { .s_video_encoding = ivtv_s_video_encoding, .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt, }; + +int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *pts = (s64)((u64)itv->last_dec_timing[2] << 32) | + (u64)itv->last_dec_timing[1]; + *frame = itv->last_dec_timing[0]; + return 0; + } + *pts = 0; + *frame = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *pts = (s64)((u64) data[2] << 32) | (u64) data[1]; + *frame = data[0]; + /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ + } + return 0; +} + +static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); + + switch (ctrl->id) { + /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME + control cluster */ + case V4L2_CID_MPEG_VIDEO_DEC_PTS: + return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64, + &itv->ctrl_frame->val64); + } + return 0; +} + +static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); + + switch (ctrl->id) { + /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK + control cluster */ + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1; + itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + break; + } + return 0; +} + +const struct v4l2_ctrl_ops ivtv_hdl_out_ops = { + .s_ctrl = ivtv_s_ctrl, + .g_volatile_ctrl = ivtv_g_volatile_ctrl, +}; diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h index d12893d..3999e63 100644 --- a/drivers/media/video/ivtv/ivtv-controls.h +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -22,5 +22,7 @@ #define IVTV_CONTROLS_H extern struct cx2341x_handler_ops ivtv_cxhdl_ops; +extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops; +int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame); #endif diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 3949b7d..679262e 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -55,7 +55,7 @@ #include "ivtv-routing.h" #include "ivtv-controls.h" #include "ivtv-gpio.h" - +#include <linux/dma-mapping.h> #include <media/tveeprom.h> #include <media/saa7115.h> #include <media/v4l2-chip-ident.h> @@ -99,7 +99,7 @@ static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, static unsigned int cardtype_c = 1; static unsigned int tuner_c = 1; -static bool radio_c = 1; +static int radio_c = 1; static unsigned int i2c_clock_period_c = 1; static char pal[] = "---"; static char secam[] = "--"; @@ -139,7 +139,7 @@ static int tunertype = -1; static int newi2c = -1; module_param_array(tuner, int, &tuner_c, 0644); -module_param_array(radio, bool, &radio_c, 0644); +module_param_array(radio, int, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); @@ -744,8 +744,6 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) itv->cur_dma_stream = -1; itv->cur_pio_stream = -1; - itv->audio_stereo_mode = AUDIO_STEREO; - itv->audio_bilingual_mode = AUDIO_MONO_LEFT; /* Ctrls */ itv->speed = 1000; @@ -815,7 +813,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_ERR("Can't enable device!\n"); return -EIO; } - if (pci_set_dma_mask(pdev, 0xffffffff)) { + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { IVTV_ERR("No suitable DMA available.\n"); return -EIO; } @@ -1200,6 +1198,32 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, itv->tuner_std = itv->std; if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler; + + itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0); + itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0); + /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported, + mask that menu item. */ + itv->ctrl_audio_playback = + v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO, + 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO); + itv->ctrl_audio_multilingual_playback = + v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops, + V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO, + 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO, + V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT); + if (hdl->error) { + retval = hdl->error; + goto free_i2c; + } + v4l2_ctrl_cluster(2, &itv->ctrl_pts); + v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback); ivtv_call_all(itv, video, s_std_output, itv->std); /* Turn off the output signal. The mpeg decoder is not yet active so without this you would get a green image until the @@ -1236,6 +1260,7 @@ free_streams: free_irq: free_irq(itv->pdev->irq, (void *)itv); free_i2c: + v4l2_ctrl_handler_free(&itv->cxhdl.hdl); exit_ivtv_i2c(itv); free_io: ivtv_iounmap(itv); @@ -1375,7 +1400,7 @@ static void ivtv_remove(struct pci_dev *pdev) else type = IVTV_DEC_STREAM_TYPE_MPG; ivtv_stop_v4l2_decode_stream(&itv->streams[type], - VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0); } ivtv_halt_firmware(itv); } @@ -1391,6 +1416,8 @@ static void ivtv_remove(struct pci_dev *pdev) ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); + v4l2_ctrl_handler_free(&itv->cxhdl.hdl); + exit_ivtv_i2c(itv); free_irq(itv->pdev->irq, (void *)itv); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 06f3d78..2e22002 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -54,7 +54,6 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/byteorder.h> #include <linux/dvb/video.h> @@ -331,6 +330,7 @@ struct ivtv_stream { struct ivtv *itv; /* for ease of use */ const char *name; /* name of the stream */ int type; /* stream type */ + u32 caps; /* V4L2 capabilities */ struct v4l2_fh *fh; /* pointer to the streaming filehandle */ spinlock_t qlock; /* locks access to the queues */ @@ -630,6 +630,16 @@ struct ivtv { struct v4l2_device v4l2_dev; struct cx2341x_handler cxhdl; + struct { + /* PTS/Frame count control cluster */ + struct v4l2_ctrl *ctrl_pts; + struct v4l2_ctrl *ctrl_frame; + }; + struct { + /* Audio Playback control cluster */ + struct v4l2_ctrl *ctrl_audio_playback; + struct v4l2_ctrl *ctrl_audio_multilingual_playback; + }; struct v4l2_ctrl_handler hdl_gpio; struct v4l2_subdev sd_gpio; /* GPIO sub-device */ u16 instance; @@ -649,7 +659,6 @@ struct ivtv { u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */ u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */ - /* Locking */ spinlock_t lock; /* lock access to this struct */ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 2cd6c89..c9663e8 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -900,7 +900,7 @@ int ivtv_v4l2_close(struct file *filp) if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT]; - ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0); /* If all output streams are closed, and if the user doesn't have IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */ diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index c4bc481..989e556 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -246,34 +246,40 @@ static int ivtv_validate_speed(int cur_speed, int new_speed) } static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, - struct video_command *vc, int try) + struct v4l2_decoder_cmd *dc, int try) { struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; - switch (vc->cmd) { - case VIDEO_CMD_PLAY: { - vc->flags = 0; - vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed); - if (vc->play.speed < 0) - vc->play.format = VIDEO_PLAY_FMT_GOP; + switch (dc->cmd) { + case V4L2_DEC_CMD_START: { + dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO; + dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed); + if (dc->start.speed < 0) + dc->start.format = V4L2_DEC_START_FMT_GOP; + else + dc->start.format = V4L2_DEC_START_FMT_NONE; + if (dc->start.speed != 500 && dc->start.speed != 1500) + dc->flags = dc->start.speed == 1000 ? 0 : + V4L2_DEC_CMD_START_MUTE_AUDIO; if (try) break; + itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO; if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG) return -EBUSY; if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) { /* forces ivtv_set_speed to be called */ itv->speed = 0; } - return ivtv_start_decoding(id, vc->play.speed); + return ivtv_start_decoding(id, dc->start.speed); } - case VIDEO_CMD_STOP: - vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK; - if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY) - vc->stop.pts = 0; + case V4L2_DEC_CMD_STOP: + dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK; + if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) + dc->stop.pts = 0; if (try) break; if (atomic_read(&itv->decoding) == 0) return 0; @@ -281,22 +287,22 @@ static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, return -EBUSY; itv->output_mode = OUT_NONE; - return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts); + return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts); - case VIDEO_CMD_FREEZE: - vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK; + case V4L2_DEC_CMD_PAUSE: + dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK; if (try) break; if (itv->output_mode != OUT_MPG) return -EBUSY; if (atomic_read(&itv->decoding) > 0) { ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, - (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0); + (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0); set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags); } break; - case VIDEO_CMD_CONTINUE: - vc->flags = 0; + case V4L2_DEC_CMD_RESUME: + dc->flags = 0; if (try) break; if (itv->output_mode != OUT_MPG) return -EBUSY; @@ -754,12 +760,15 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { - struct ivtv *itv = fh2id(fh)->itv; + struct ivtv_open_id *id = fh2id(file->private_data); + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev)); - vcap->capabilities = itv->v4l2_cap; /* capabilities */ + vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS; + vcap->device_caps = s->caps; return 0; } @@ -1476,8 +1485,6 @@ static int ivtv_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - IVTV_INFO("================= START STATUS CARD #%d =================\n", - itv->instance); IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); if (itv->hw_flags & IVTV_HW_TVEEPROM) { struct tveeprom tv; @@ -1501,13 +1508,6 @@ static int ivtv_log_status(struct file *file, void *fh) "YUV Frames", "Passthrough", }; - static const char * const audio_modes[5] = { - "Stereo", - "Left", - "Right", - "Mono", - "Swapped" - }; static const char * const alpha_mode[4] = { "None", "Global", @@ -1536,9 +1536,6 @@ static int ivtv_log_status(struct file *file, void *fh) ivtv_get_output(itv, itv->active_output, &vidout); ivtv_get_audio_output(itv, 0, &audout); IVTV_INFO("Video Output: %s\n", vidout.name); - IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name, - audio_modes[itv->audio_stereo_mode], - audio_modes[itv->audio_bilingual_mode]); if (mode < 0 || mode > OUT_PASSTHROUGH) mode = OUT_NONE; IVTV_INFO("Output Mode: %s\n", output_modes[mode]); @@ -1566,12 +1563,27 @@ static int ivtv_log_status(struct file *file, void *fh) IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); - IVTV_INFO("================== END STATUS CARD #%d ==================\n", - itv->instance); - return 0; } +static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec) +{ + struct ivtv_open_id *id = fh2id(file->private_data); + struct ivtv *itv = id->itv; + + IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd); + return ivtv_video_command(itv, id, dec, false); +} + +static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec) +{ + struct ivtv_open_id *id = fh2id(file->private_data); + struct ivtv *itv = id->itv; + + IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd); + return ivtv_video_command(itv, id, dec, true); +} + static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) { struct ivtv_open_id *id = fh2id(filp->private_data); @@ -1605,9 +1617,15 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) return ivtv_yuv_prep_frame(itv, args); } + case IVTV_IOC_PASSTHROUGH_MODE: + IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, *(int *)arg != 0); + case VIDEO_GET_PTS: { - u32 data[CX2341X_MBOX_MAX_DATA]; - u64 *pts = arg; + s64 *pts = arg; + s64 frame; IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n"); if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { @@ -1616,29 +1634,12 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) } if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; - - if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { - *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) | - (u64)itv->last_dec_timing[1]; - break; - } - *pts = 0; - if (atomic_read(&itv->decoding)) { - if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { - IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); - return -EIO; - } - memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); - set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); - *pts = (u64) ((u64) data[2] << 32) | (u64) data[1]; - /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ - } - break; + return ivtv_g_pts_frame(itv, pts, &frame); } case VIDEO_GET_FRAME_COUNT: { - u32 data[CX2341X_MBOX_MAX_DATA]; - u64 *frame = arg; + s64 *frame = arg; + s64 pts; IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n"); if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { @@ -1647,71 +1648,58 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) } if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; - - if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { - *frame = itv->last_dec_timing[0]; - break; - } - *frame = 0; - if (atomic_read(&itv->decoding)) { - if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { - IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); - return -EIO; - } - memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); - set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); - *frame = data[0]; - } - break; + return ivtv_g_pts_frame(itv, &pts, frame); } case VIDEO_PLAY: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_PLAY\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_PLAY; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_START; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_STOP: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_STOP\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_STOP; - vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_STOP; + dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_FREEZE: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_FREEZE; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_PAUSE; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_CONTINUE: { - struct video_command vc; + struct v4l2_decoder_cmd dc; IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n"); - memset(&vc, 0, sizeof(vc)); - vc.cmd = VIDEO_CMD_CONTINUE; - return ivtv_video_command(itv, id, &vc, 0); + memset(&dc, 0, sizeof(dc)); + dc.cmd = V4L2_DEC_CMD_RESUME; + return ivtv_video_command(itv, id, &dc, 0); } case VIDEO_COMMAND: case VIDEO_TRY_COMMAND: { - struct video_command *vc = arg; + /* Note: struct v4l2_decoder_cmd has the same layout as + struct video_command */ + struct v4l2_decoder_cmd *dc = arg; int try = (cmd == VIDEO_TRY_COMMAND); if (try) - IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", vc->cmd); + IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd); else - IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", vc->cmd); - return ivtv_video_command(itv, id, vc, try); + IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd); + return ivtv_video_command(itv, id, dc, try); } case VIDEO_GET_EVENT: { @@ -1775,17 +1763,13 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) 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; + return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1); 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; + return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1); default: return -EINVAL; @@ -1800,6 +1784,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, if (!valid_prio) { switch (cmd) { + case IVTV_IOC_PASSTHROUGH_MODE: case VIDEO_PLAY: case VIDEO_STOP: case VIDEO_FREEZE: @@ -1825,6 +1810,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, } case IVTV_IOC_DMA_FRAME: + case IVTV_IOC_PASSTHROUGH_MODE: case VIDEO_GET_PTS: case VIDEO_GET_FRAME_COUNT: case VIDEO_GET_EVENT: @@ -1889,6 +1875,8 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap, .vidioc_encoder_cmd = ivtv_encoder_cmd, .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd, + .vidioc_decoder_cmd = ivtv_decoder_cmd, + .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd, .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap, diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index c6e28b4..7ea5ca7 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -78,60 +78,73 @@ static struct { int num_offset; int dma, pio; enum v4l2_buf_type buf_type; + u32 v4l2_caps; const struct v4l2_file_operations *fops; } ivtv_stream_info[] = { { /* IVTV_ENC_STREAM_TYPE_MPG */ "encoder MPG", VFL_TYPE_GRABBER, 0, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_YUV */ "encoder YUV", VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_VBI */ "encoder VBI", VFL_TYPE_VBI, 0, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_PCM */ "encoder PCM", VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE, + V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_ENC_STREAM_TYPE_RAD */ "encoder radio", VFL_TYPE_RADIO, 0, PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE, + V4L2_CAP_RADIO | V4L2_CAP_TUNER, &ivtv_v4l2_enc_fops }, { /* IVTV_DEC_STREAM_TYPE_MPG */ "decoder MPG", VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops }, { /* IVTV_DEC_STREAM_TYPE_VBI */ "decoder VBI", VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops }, { /* IVTV_DEC_STREAM_TYPE_VOUT */ "decoder VOUT", VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT, + V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops }, { /* IVTV_DEC_STREAM_TYPE_YUV */ "decoder YUV", VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops } }; @@ -149,6 +162,7 @@ static void ivtv_stream_init(struct ivtv *itv, int type) s->itv = itv; s->type = type; s->name = ivtv_stream_info[type].name; + s->caps = ivtv_stream_info[type].v4l2_caps; if (ivtv_stream_info[type].pio) s->dma = PCI_DMA_NONE; @@ -209,8 +223,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->num = num; s->vdev->v4l2_dev = &itv->v4l2_dev; - s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->fops = ivtv_stream_info[type].fops; + s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; s->vdev->lock = &itv->serialize_lock; @@ -891,7 +905,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags); /* Stop Decoder */ - if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) { + if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) { u32 tmp = 0; /* Wait until the decoder is no longer running */ @@ -911,7 +925,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) break; } } - ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0); + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0); /* turn off notification of dual/stereo mode change */ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index 69cc816..7338cb2 100644 --- a/drivers/media/video/ivtv/ivtv-udma.c +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -57,9 +57,9 @@ int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info if (dma->bouncemap[map_offset] == NULL) return -1; local_irq_save(flags); - src = kmap_atomic(dma->map[map_offset], KM_BOUNCE_READ) + offset; + src = kmap_atomic(dma->map[map_offset]) + offset; memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len); - kunmap_atomic(src, KM_BOUNCE_READ); + kunmap_atomic(src); local_irq_restore(flags); sg_set_page(&dma->SGlist[map_offset], dma->bouncemap[map_offset], len, offset); } diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index d0fbfcf..e5e7fa9 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -1293,7 +1293,6 @@ static int __init ivtvfb_init(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); - put_driver(drv); if (!registered) { printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; @@ -1310,7 +1309,6 @@ static void ivtvfb_cleanup(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); - put_driver(drv); } module_init(ivtvfb_init); diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c index afa9118..ee7ca2d 100644 --- a/drivers/media/video/ks0127.c +++ b/drivers/media/video/ks0127.c @@ -721,15 +721,4 @@ static struct i2c_driver ks0127_driver = { .id_table = ks0127_id, }; -static __init int init_ks0127(void) -{ - return i2c_add_driver(&ks0127_driver); -} - -static __exit void exit_ks0127(void) -{ - i2c_del_driver(&ks0127_driver); -} - -module_init(init_ks0127); -module_exit(exit_ks0127); +module_i2c_driver(ks0127_driver); diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 303ffa7..0991576 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -213,15 +213,4 @@ static struct i2c_driver m52790_driver = { .id_table = m52790_id, }; -static __init int init_m52790(void) -{ - return i2c_add_driver(&m52790_driver); -} - -static __exit void exit_m52790(void) -{ - i2c_del_driver(&m52790_driver); -} - -module_init(init_m52790); -module_exit(exit_m52790); +module_i2c_driver(m52790_driver); diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index 93d768d..d718aee 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -982,8 +982,8 @@ static int __devinit m5mols_probe(struct i2c_client *client, } sd = &info->sd; - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &m5mols_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->internal_ops = &m5mols_subdev_internal_ops; @@ -1057,18 +1057,7 @@ static struct i2c_driver m5mols_i2c_driver = { .id_table = m5mols_id, }; -static int __init m5mols_mod_init(void) -{ - return i2c_add_driver(&m5mols_i2c_driver); -} - -static void __exit m5mols_mod_exit(void) -{ - i2c_del_driver(&m5mols_i2c_driver); -} - -module_init(m5mols_mod_init); -module_exit(m5mols_mod_exit); +module_i2c_driver(m5mols_i2c_driver); MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c index 37d20e7..996ac34 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.c +++ b/drivers/media/video/marvell-ccic/mcam-core.c @@ -509,11 +509,17 @@ static void mcam_sg_next_buffer(struct mcam_camera *cam) buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); list_del_init(&buf->queue); + /* + * Very Bad Not Good Things happen if you don't clear + * C1_DESC_ENA before making any descriptor changes. + */ + mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa); mcam_reg_write(cam, REG_DESC_LEN_Y, buf->dma_desc_nent*sizeof(struct mcam_dma_desc)); mcam_reg_write(cam, REG_DESC_LEN_U, 0); mcam_reg_write(cam, REG_DESC_LEN_V, 0); + mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); cam->vb_bufs[0] = buf; } @@ -533,7 +539,6 @@ static void mcam_ctlr_dma_sg(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD); mcam_sg_next_buffer(cam); - mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); cam->nbufs = 3; } @@ -556,17 +561,16 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) struct mcam_vb_buffer *buf = cam->vb_bufs[0]; /* - * Very Bad Not Good Things happen if you don't clear - * C1_DESC_ENA before making any descriptor changes. + * If we're no longer supposed to be streaming, don't do anything. */ - mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); + if (cam->state != S_STREAMING) + return; /* * If we have another buffer available, put it in and * restart the engine. */ if (!list_empty(&cam->buffers)) { mcam_sg_next_buffer(cam); - mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); mcam_ctlr_start(cam); /* * Otherwise set CF_SG_RESTART and the controller will @@ -737,7 +741,14 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam) mcam_ctlr_stop(cam); cam->state = S_IDLE; spin_unlock_irqrestore(&cam->dev_lock, flags); - msleep(40); + /* + * This is a brutally long sleep, but experience shows that + * it can take the controller a while to get the message that + * it needs to stop grabbing frames. In particular, we can + * sometimes (on mmp) get a frame at the end WITHOUT the + * start-of-frame indication. + */ + msleep(150); if (test_bit(CF_DMA_ACTIVE, &cam->flags)) cam_err(cam, "Timeout waiting for DMA to end\n"); /* This would be bad news - what now? */ @@ -880,6 +891,7 @@ static int mcam_read_setup(struct mcam_camera *cam) * Turn it loose. */ spin_lock_irqsave(&cam->dev_lock, flags); + clear_bit(CF_DMA_ACTIVE, &cam->flags); mcam_reset_buffers(cam); mcam_ctlr_irq_enable(cam); cam->state = S_STREAMING; @@ -922,7 +934,7 @@ static void mcam_vb_buf_queue(struct vb2_buffer *vb) spin_lock_irqsave(&cam->dev_lock, flags); start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers); list_add(&mvb->queue, &cam->buffers); - if (test_bit(CF_SG_RESTART, &cam->flags)) + if (cam->state == S_STREAMING && test_bit(CF_SG_RESTART, &cam->flags)) mcam_sg_restart(cam); spin_unlock_irqrestore(&cam->dev_lock, flags); if (start) @@ -1555,15 +1567,12 @@ static int mcam_v4l_release(struct file *filp) { struct mcam_camera *cam = filp->private_data; - cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames, + cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames, singles, delivered); mutex_lock(&cam->s_mutex); (cam->users)--; - if (filp == cam->owner) { - mcam_ctlr_stop_dma(cam); - cam->owner = NULL; - } if (cam->users == 0) { + mcam_ctlr_stop_dma(cam); mcam_cleanup_vb2(cam); mcam_ctlr_power_down(cam); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) @@ -1688,6 +1697,8 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs) if (irqs & (IRQ_EOF0 << frame)) { mcam_frame_complete(cam, frame); handled = 1; + if (cam->buffer_mode == B_DMA_sg) + break; } /* * If a frame starts, note that we have DMA active. This diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/video/marvell-ccic/mcam-core.h index 917200e..bd6acba 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.h +++ b/drivers/media/video/marvell-ccic/mcam-core.h @@ -107,7 +107,6 @@ struct mcam_camera { enum mcam_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ - struct file *owner; /* Who has data access (v4l2) */ /* * Subsystem structures. diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c index 0d64e2d..d235523 100644 --- a/drivers/media/video/marvell-ccic/mmp-driver.c +++ b/drivers/media/video/marvell-ccic/mmp-driver.c @@ -106,6 +106,13 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) /* * Power control. */ +static void mmpcam_power_up_ctlr(struct mmp_camera *cam) +{ + iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); + iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); + mdelay(1); +} + static void mmpcam_power_up(struct mcam_camera *mcam) { struct mmp_camera *cam = mcam_to_cam(mcam); @@ -113,9 +120,7 @@ static void mmpcam_power_up(struct mcam_camera *mcam) /* * Turn on power and clocks to the controller. */ - iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); - iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); - mdelay(1); + mmpcam_power_up_ctlr(cam); /* * Provide power to the sensor. */ @@ -335,7 +340,7 @@ static int mmpcam_resume(struct platform_device *pdev) * touch a register even if nothing was active before; trust * me, it's better this way. */ - mmpcam_power_up(&cam->mcam); + mmpcam_power_up_ctlr(cam); return mccic_resume(&cam->mcam); } diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index d7cd0f6..82ce507 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -881,18 +881,7 @@ static struct i2c_driver msp_driver = { .id_table = msp_id, }; -static __init int init_msp(void) -{ - return i2c_add_driver(&msp_driver); -} - -static __exit void exit_msp(void) -{ - i2c_del_driver(&msp_driver); -} - -module_init(init_msp); -module_exit(exit_msp); +module_i2c_driver(msp_driver); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 097c9d3..7e64818 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -730,18 +730,7 @@ static struct i2c_driver mt9m001_i2c_driver = { .id_table = mt9m001_id, }; -static int __init mt9m001_mod_init(void) -{ - return i2c_add_driver(&mt9m001_i2c_driver); -} - -static void __exit mt9m001_mod_exit(void) -{ - i2c_del_driver(&mt9m001_i2c_driver); -} - -module_init(mt9m001_mod_init); -module_exit(mt9m001_mod_exit); +module_i2c_driver(mt9m001_i2c_driver); MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c new file mode 100644 index 0000000..645973c --- /dev/null +++ b/drivers/media/video/mt9m032.c @@ -0,0 +1,869 @@ +/* + * Driver for MT9M032 CMOS Image Sensor from Micron + * + * Copyright (C) 2010-2011 Lund Engineering + * Contact: Gil Lund <gwlund@lundeng.com> + * Author: Martin Hostettler <martin@neutronstar.dyndns.org> + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> + +#include <media/media-entity.h> +#include <media/mt9m032.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "aptina-pll.h" + +/* + * width and height include active boundary and black parts + * + * column 0- 15 active boundary + * column 16-1455 image + * column 1456-1471 active boundary + * column 1472-1599 black + * + * row 0- 51 black + * row 53- 59 active boundary + * row 60-1139 image + * row 1140-1147 active boundary + * row 1148-1151 black + */ + +#define MT9M032_PIXEL_ARRAY_WIDTH 1600 +#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 + +#define MT9M032_CHIP_VERSION 0x00 +#define MT9M032_CHIP_VERSION_VALUE 0x1402 +#define MT9M032_ROW_START 0x01 +#define MT9M032_ROW_START_MIN 0 +#define MT9M032_ROW_START_MAX 1152 +#define MT9M032_ROW_START_DEF 60 +#define MT9M032_COLUMN_START 0x02 +#define MT9M032_COLUMN_START_MIN 0 +#define MT9M032_COLUMN_START_MAX 1600 +#define MT9M032_COLUMN_START_DEF 16 +#define MT9M032_ROW_SIZE 0x03 +#define MT9M032_ROW_SIZE_MIN 32 +#define MT9M032_ROW_SIZE_MAX 1152 +#define MT9M032_ROW_SIZE_DEF 1080 +#define MT9M032_COLUMN_SIZE 0x04 +#define MT9M032_COLUMN_SIZE_MIN 32 +#define MT9M032_COLUMN_SIZE_MAX 1600 +#define MT9M032_COLUMN_SIZE_DEF 1440 +#define MT9M032_HBLANK 0x05 +#define MT9M032_VBLANK 0x06 +#define MT9M032_VBLANK_MAX 0x7ff +#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 +#define MT9M032_SHUTTER_WIDTH_LOW 0x09 +#define MT9M032_SHUTTER_WIDTH_MIN 1 +#define MT9M032_SHUTTER_WIDTH_MAX 1048575 +#define MT9M032_SHUTTER_WIDTH_DEF 1943 +#define MT9M032_PIX_CLK_CTRL 0x0a +#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 +#define MT9M032_RESTART 0x0b +#define MT9M032_RESET 0x0d +#define MT9M032_PLL_CONFIG1 0x11 +#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f +#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 +#define MT9M032_READ_MODE1 0x1e +#define MT9M032_READ_MODE2 0x20 +#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 +#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 +#define MT9M032_READ_MODE2_ROW_BLC 0x40 +#define MT9M032_GAIN_GREEN1 0x2b +#define MT9M032_GAIN_BLUE 0x2c +#define MT9M032_GAIN_RED 0x2d +#define MT9M032_GAIN_GREEN2 0x2e + +/* write only */ +#define MT9M032_GAIN_ALL 0x35 +#define MT9M032_GAIN_DIGITAL_MASK 0x7f +#define MT9M032_GAIN_DIGITAL_SHIFT 8 +#define MT9M032_GAIN_AMUL_SHIFT 6 +#define MT9M032_GAIN_ANALOG_MASK 0x3f +#define MT9M032_FORMATTER1 0x9e +#define MT9M032_FORMATTER2 0x9f +#define MT9M032_FORMATTER2_DOUT_EN 0x1000 +#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 + +/* + * The available MT9M032 datasheet is missing documentation for register 0x10 + * MT9P031 seems to be close enough, so use constants from that datasheet for + * now. + * But keep the name MT9P031 to remind us, that this isn't really confirmed + * for this sensor. + */ +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG2 0x11 +#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f + +struct mt9m032 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct mt9m032_platform_data *pdata; + + unsigned int pix_clock; + + struct v4l2_ctrl_handler ctrls; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + + struct mutex lock; /* Protects streaming, format, interval and crop */ + + bool streaming; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + struct v4l2_fract frame_interval; +}; + +#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) +#define to_dev(sensor) \ + (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) + +static int mt9m032_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) +{ + unsigned int effective_width; + u32 ns; + + effective_width = width + 716; /* empirical value */ + ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); + dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); + return ns; +} + +static int mt9m032_update_timing(struct mt9m032 *sensor, + struct v4l2_fract *interval) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct v4l2_rect *crop = &sensor->crop; + unsigned int min_vblank; + unsigned int vblank; + u32 row_time; + + if (!interval) + interval = &sensor->frame_interval; + + row_time = mt9m032_row_time(sensor, crop->width); + + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + + if (vblank > MT9M032_VBLANK_MAX) { + /* hardware limits to 11 bit values */ + interval->denominator = 1000; + interval->numerator = + div_u64((crop->height + MT9M032_VBLANK_MAX) * + (u64)row_time * interval->denominator, + 1000000000ULL); + vblank = div_u64(1000000000ULL * interval->numerator, + (u64)row_time * interval->denominator) + - crop->height; + } + /* enforce minimal 1.6ms blanking time. */ + min_vblank = 1600000 / row_time; + vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); + + return mt9m032_write(client, MT9M032_VBLANK, vblank); +} + +static int mt9m032_update_geom_timing(struct mt9m032 *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, + sensor->crop.width - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_SIZE, + sensor->crop.height - 1); + if (!ret) + ret = mt9m032_write(client, MT9M032_COLUMN_START, + sensor->crop.left); + if (!ret) + ret = mt9m032_write(client, MT9M032_ROW_START, + sensor->crop.top); + if (!ret) + ret = mt9m032_update_timing(sensor, NULL); + return ret; +} + +static int update_formatter2(struct mt9m032 *sensor, bool streaming) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + u16 reg_val = MT9M032_FORMATTER2_DOUT_EN + | 0x0070; /* parts reserved! */ + /* possibly for changing to 14-bit mode */ + + if (streaming) + reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ + + return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); +} + +static int mt9m032_setup_pll(struct mt9m032 *sensor) +{ + static const struct aptina_pll_limits limits = { + .ext_clock_min = 8000000, + .ext_clock_max = 16500000, + .int_clock_min = 2000000, + .int_clock_max = 24000000, + .out_clock_min = 322000000, + .out_clock_max = 693000000, + .pix_clock_max = 99000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + struct mt9m032_platform_data *pdata = sensor->pdata; + struct aptina_pll pll; + int ret; + + pll.ext_clock = pdata->ext_clock; + pll.pix_clock = pdata->pix_clock; + + ret = aptina_pll_calculate(&client->dev, &limits, &pll); + if (ret < 0) + return ret; + + sensor->pix_clock = pdata->pix_clock; + + ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, + (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) + | (pll.p1 - 1)); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); + if (!ret) + ret = mt9m032_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + if (!ret) /* more reserved, Continuous, Master Mode */ + ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); + if (!ret) /* Set 14-bit mode, select 7 divider */ + ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Subdev pad operations + */ + +static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_Y8_1X8; + return 0; +} + +static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) + return -EINVAL; + + fse->min_width = MT9M032_COLUMN_SIZE_DEF; + fse->max_width = MT9M032_COLUMN_SIZE_DEF; + fse->min_height = MT9M032_ROW_SIZE_DEF; + fse->max_height = MT9M032_ROW_SIZE_DEF; + + return 0; +} + +/** + * __mt9m032_get_pad_crop() - get crop rect + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try crop rect from + * @which: select try or active crop rect + * + * Returns a pointer the current active or fh relative try crop rect + */ +static struct v4l2_rect * +__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->crop; + default: + return NULL; + } +} + +/** + * __mt9m032_get_pad_format() - get format + * @sensor: pointer to the sensor struct + * @fh: file handle for getting the try format from + * @which: select try or active format + * + * Returns a pointer the current active or fh relative try format + */ +static struct v4l2_mbus_framefmt * +__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, 0); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->format; + default: + return NULL; + } +} + +static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Scaling is not supported, the format is thus fixed. */ + fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); + ret = 0; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto done; + } + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, + MT9M032_COLUMN_START_MAX); + rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, + MT9M032_ROW_START_MAX); + rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, + MT9M032_COLUMN_SIZE_MAX); + rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, + MT9M032_ROW_SIZE_MAX); + + rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); + + __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __mt9m032_get_pad_format(sensor, fh, crop->which); + format->width = rect.width; + format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ret = mt9m032_update_geom_timing(sensor); + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + + mutex_lock(&sensor->lock); + memset(fi, 0, sizeof(*fi)); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto done; + } + + /* Avoid divisions by 0. */ + if (fi->interval.denominator == 0) + fi->interval.denominator = 1; + + ret = mt9m032_update_timing(sensor, &fi->interval); + if (!ret) + sensor->frame_interval = fi->interval; + +done: + mutex_unlock(&sensor->lock); + return ret; +} + +static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) +{ + struct mt9m032 *sensor = to_mt9m032(subdev); + int ret; + + mutex_lock(&sensor->lock); + ret = update_formatter2(sensor, streaming); + if (!ret) + sensor->streaming = streaming; + mutex_unlock(&sensor->lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m032_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int val; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + if (reg->match.addr != client->addr) + return -ENODEV; + + val = mt9m032_read(client, reg->reg); + if (val < 0) + return -EIO; + + reg->size = 2; + reg->val = val; + + return 0; +} + +static int mt9m032_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct mt9m032 *sensor = to_mt9m032(sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + return mt9m032_write(client, reg->reg, reg->val); +} +#endif + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) + | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) + | MT9M032_READ_MODE2_ROW_BLC + | 0x0007; + + return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); +} + +static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int digital_gain_val; /* in 1/8th (0..127) */ + int analog_mul; /* 0 or 1 */ + int analog_gain_val; /* in 1/16th. (0..63) */ + u16 reg_val; + + digital_gain_val = 51; /* from setup example */ + + if (val < 63) { + analog_mul = 0; + analog_gain_val = val; + } else { + analog_mul = 1; + analog_gain_val = val / 2; + } + + /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ + /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ + + reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) + << MT9M032_GAIN_DIGITAL_SHIFT) + | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) + | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); + + return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); +} + +static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { + /* round because of multiplier used for values >= 63 */ + ctrl->val &= ~1; + } + + return 0; +} + +static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m032 *sensor = + container_of(ctrl->handler, struct mt9m032, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + return mt9m032_set_gain(sensor, ctrl->val); + + case V4L2_CID_HFLIP: + /* case V4L2_CID_VFLIP: -- In the same cluster */ + return update_read_mode2(sensor, sensor->vflip->val, + sensor->hflip->val); + + case V4L2_CID_EXPOSURE: + ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { + .s_ctrl = mt9m032_set_ctrl, + .try_ctrl = mt9m032_try_ctrl, +}; + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops mt9m032_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m032_g_register, + .s_register = mt9m032_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops mt9m032_video_ops = { + .s_stream = mt9m032_s_stream, + .g_frame_interval = mt9m032_get_frame_interval, + .s_frame_interval = mt9m032_set_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { + .enum_mbus_code = mt9m032_enum_mbus_code, + .enum_frame_size = mt9m032_enum_frame_size, + .get_fmt = mt9m032_get_pad_format, + .set_fmt = mt9m032_set_pad_format, + .set_crop = mt9m032_set_pad_crop, + .get_crop = mt9m032_get_pad_crop, +}; + +static const struct v4l2_subdev_ops mt9m032_ops = { + .core = &mt9m032_core_ops, + .video = &mt9m032_video_ops, + .pad = &mt9m032_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9m032_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct i2c_adapter *adapter = client->adapter; + struct mt9m032 *sensor; + int chip_version; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + if (!client->dev.platform_data) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + mutex_init(&sensor->lock); + + sensor->pdata = client->dev.platform_data; + + v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); + if (chip_version != MT9M032_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9M032 not detected, wrong version " + "0x%04x\n", chip_version); + ret = -ENODEV; + goto error_sensor; + } + + dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", + client->addr); + + sensor->frame_interval.numerator = 1; + sensor->frame_interval.denominator = 30; + + sensor->crop.left = MT9M032_COLUMN_START_DEF; + sensor->crop.top = MT9M032_ROW_START_DEF; + sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; + sensor->crop.height = MT9M032_ROW_SIZE_DEF; + + sensor->format.width = sensor->crop.width; + sensor->format.height = sensor->crop.height; + sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; + sensor->format.field = V4L2_FIELD_NONE; + sensor->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_ctrl_handler_init(&sensor->ctrls, 4); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, + &mt9m032_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, + V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, + MT9M032_SHUTTER_WIDTH_MAX, 1, + MT9M032_SHUTTER_WIDTH_DEF); + + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "control initialization error %d\n", ret); + goto error_ctrl; + } + + v4l2_ctrl_cluster(2, &sensor->hflip); + + sensor->subdev.ctrl_handler = &sensor->ctrls; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); + if (ret < 0) + goto error_ctrl; + + ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ + if (ret < 0) + goto error_entity; + mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ + if (ret < 0) + goto error_entity; + + ret = mt9m032_setup_pll(sensor); + if (ret < 0) + goto error_entity; + usleep_range(10000, 11000); + + ret = v4l2_ctrl_handler_setup(&sensor->ctrls); + if (ret < 0) + goto error_entity; + + /* SIZE */ + ret = mt9m032_update_geom_timing(sensor); + if (ret < 0) + goto error_entity; + + ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ + if (ret < 0) + goto error_entity; + ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ + if (ret < 0) + goto error_entity; + if (sensor->pdata->invert_pixclock) { + ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, + MT9M032_PIX_CLK_CTRL_INV_PIXCLK); + if (ret < 0) + goto error_entity; + } + + ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ + if (ret < 0) + goto error_entity; + msleep(100); + ret = update_formatter2(sensor, false); + if (ret < 0) + goto error_entity; + + return ret; + +error_entity: + media_entity_cleanup(&sensor->subdev.entity); +error_ctrl: + v4l2_ctrl_handler_free(&sensor->ctrls); +error_sensor: + mutex_destroy(&sensor->lock); + kfree(sensor); + return ret; +} + +static int mt9m032_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9m032 *sensor = to_mt9m032(subdev); + + v4l2_device_unregister_subdev(&sensor->subdev); + v4l2_ctrl_handler_free(&sensor->ctrls); + media_entity_cleanup(&sensor->subdev.entity); + mutex_destroy(&sensor->lock); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id mt9m032_id_table[] = { + { MT9M032_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); + +static struct i2c_driver mt9m032_i2c_driver = { + .driver = { + .name = MT9M032_NAME, + }, + .probe = mt9m032_probe, + .remove = mt9m032_remove, + .id_table = mt9m032_id_table, +}; + +module_i2c_driver(mt9m032_i2c_driver); + +MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>"); +MODULE_DESCRIPTION("MT9M032 camera sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index bee65bf..b0c5299 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -1008,18 +1008,7 @@ static struct i2c_driver mt9m111_i2c_driver = { .id_table = mt9m111_id, }; -static int __init mt9m111_mod_init(void) -{ - return i2c_add_driver(&mt9m111_i2c_driver); -} - -static void __exit mt9m111_mod_exit(void) -{ - i2c_del_driver(&mt9m111_i2c_driver); -} - -module_init(mt9m111_mod_init); -module_exit(mt9m111_mod_exit); +module_i2c_driver(mt9m111_i2c_driver); MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver"); MODULE_AUTHOR("Robert Jarzmik"); diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index 93c3ec7..c81eaf4 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -19,7 +19,6 @@ #include <linux/log2.h> #include <linux/pm.h> #include <linux/slab.h> -#include <media/v4l2-subdev.h> #include <linux/videodev2.h> #include <media/mt9p031.h> @@ -28,6 +27,8 @@ #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> +#include "aptina-pll.h" + #define MT9P031_PIXEL_ARRAY_WIDTH 2752 #define MT9P031_PIXEL_ARRAY_HEIGHT 2004 @@ -98,14 +99,6 @@ #define MT9P031_TEST_PATTERN_RED 0xa2 #define MT9P031_TEST_PATTERN_BLUE 0xa3 -struct mt9p031_pll_divs { - u32 ext_freq; - u32 target_freq; - u8 m; - u8 n; - u8 p1; -}; - struct mt9p031 { struct v4l2_subdev subdev; struct media_pad pad; @@ -115,10 +108,8 @@ struct mt9p031 { struct mt9p031_platform_data *pdata; struct mutex power_lock; /* lock to protect power_count */ int power_count; - u16 xskip; - u16 yskip; - const struct mt9p031_pll_divs *pll; + struct aptina_pll pll; /* Registers cache */ u16 output_control; @@ -186,33 +177,31 @@ static int mt9p031_reset(struct mt9p031 *mt9p031) 0); } -/* - * This static table uses ext_freq and vdd_io values to select suitable - * PLL dividers m, n and p1 which have been calculated as specifiec in p36 - * of Aptina's mt9p031 datasheet. New values should be added here. - */ -static const struct mt9p031_pll_divs mt9p031_divs[] = { - /* ext_freq target_freq m n p1 */ - {21000000, 48000000, 26, 2, 6} -}; - -static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031) +static int mt9p031_pll_setup(struct mt9p031 *mt9p031) { + static const struct aptina_pll_limits limits = { + .ext_clock_min = 6000000, + .ext_clock_max = 27000000, + .int_clock_min = 2000000, + .int_clock_max = 13500000, + .out_clock_min = 180000000, + .out_clock_max = 360000000, + .pix_clock_max = 96000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 255, + .p1_min = 1, + .p1_max = 128, + }; + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); - int i; + struct mt9p031_platform_data *pdata = mt9p031->pdata; - for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) { - if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq && - mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) { - mt9p031->pll = &mt9p031_divs[i]; - return 0; - } - } + mt9p031->pll.ext_clock = pdata->ext_freq; + mt9p031->pll.pix_clock = pdata->target_freq; - dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, " - "target_freq = %d\n", mt9p031->pdata->ext_freq, - mt9p031->pdata->target_freq); - return -EINVAL; + return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); } static int mt9p031_pll_enable(struct mt9p031 *mt9p031) @@ -226,11 +215,11 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031) return ret; ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, - (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1)); + (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); if (ret < 0) return ret; - ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1); + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); if (ret < 0) return ret; @@ -785,8 +774,6 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) format->field = V4L2_FIELD_NONE; format->colorspace = V4L2_COLORSPACE_SRGB; - mt9p031->xskip = 1; - mt9p031->yskip = 1; return mt9p031_set_power(subdev, 1); } @@ -905,7 +892,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; - ret = mt9p031_pll_get_divs(mt9p031); + ret = mt9p031_pll_setup(mt9p031); done: if (ret < 0) { @@ -945,18 +932,7 @@ static struct i2c_driver mt9p031_i2c_driver = { .id_table = mt9p031_id, }; -static int __init mt9p031_mod_init(void) -{ - return i2c_add_driver(&mt9p031_i2c_driver); -} - -static void __exit mt9p031_mod_exit(void) -{ - i2c_del_driver(&mt9p031_i2c_driver); -} - -module_init(mt9p031_mod_init); -module_exit(mt9p031_mod_exit); +module_i2c_driver(mt9p031_i2c_driver); MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c index cd81d04a..49ca3cb 100644 --- a/drivers/media/video/mt9t001.c +++ b/drivers/media/video/mt9t001.c @@ -817,18 +817,7 @@ static struct i2c_driver mt9t001_driver = { .id_table = mt9t001_id, }; -static int __init mt9t001_init(void) -{ - return i2c_add_driver(&mt9t001_driver); -} - -static void __exit mt9t001_exit(void) -{ - i2c_del_driver(&mt9t001_driver); -} - -module_init(mt9t001_init); -module_exit(mt9t001_exit); +module_i2c_driver(mt9t001_driver); MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 84add1a..1415074 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -850,18 +850,7 @@ static struct i2c_driver mt9t031_i2c_driver = { .id_table = mt9t031_id, }; -static int __init mt9t031_mod_init(void) -{ - return i2c_add_driver(&mt9t031_i2c_driver); -} - -static void __exit mt9t031_mod_exit(void) -{ - i2c_del_driver(&mt9t031_i2c_driver); -} - -module_init(mt9t031_mod_init); -module_exit(mt9t031_mod_exit); +module_i2c_driver(mt9t031_i2c_driver); MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index 7b34b11..8d1445f 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -1117,21 +1117,7 @@ static struct i2c_driver mt9t112_i2c_driver = { .id_table = mt9t112_id, }; -/************************************************************************ - module function -************************************************************************/ -static int __init mt9t112_module_init(void) -{ - return i2c_add_driver(&mt9t112_i2c_driver); -} - -static void __exit mt9t112_module_exit(void) -{ - i2c_del_driver(&mt9t112_i2c_driver); -} - -module_init(mt9t112_module_init); -module_exit(mt9t112_module_exit); +module_i2c_driver(mt9t112_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for mt9t112"); MODULE_AUTHOR("Kuninori Morimoto"); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index db74dd2..6bf01ad 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -709,15 +709,4 @@ static struct i2c_driver mt9v011_driver = { .id_table = mt9v011_id, }; -static __init int init_mt9v011(void) -{ - return i2c_add_driver(&mt9v011_driver); -} - -static __exit void exit_mt9v011(void) -{ - i2c_del_driver(&mt9v011_driver); -} - -module_init(init_mt9v011); -module_exit(exit_mt9v011); +module_i2c_driver(mt9v011_driver); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 9449407..bf63417 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -872,18 +872,7 @@ static struct i2c_driver mt9v022_i2c_driver = { .id_table = mt9v022_id, }; -static int __init mt9v022_mod_init(void) -{ - return i2c_add_driver(&mt9v022_i2c_driver); -} - -static void __exit mt9v022_mod_exit(void) -{ - i2c_del_driver(&mt9v022_i2c_driver); -} - -module_init(mt9v022_mod_init); -module_exit(mt9v022_mod_exit); +module_i2c_driver(mt9v022_i2c_driver); MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c index d90b982..75e253a 100644 --- a/drivers/media/video/mt9v032.c +++ b/drivers/media/video/mt9v032.c @@ -756,18 +756,7 @@ static struct i2c_driver mt9v032_driver = { .id_table = mt9v032_id, }; -static int __init mt9v032_init(void) -{ - return i2c_add_driver(&mt9v032_driver); -} - -static void __exit mt9v032_exit(void) -{ - i2c_del_driver(&mt9v032_driver); -} - -module_init(mt9v032_init); -module_exit(mt9v032_exit); +module_i2c_driver(mt9v032_driver); MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 04aab0c..18afaee 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008, Sascha Hauer, Pengutronix * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography + * Copyright (C) 2012, Javier Martin, Vista Silicon S.L. * * 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 @@ -18,6 +19,7 @@ #include <linux/dma-mapping.h> #include <linux/errno.h> #include <linux/fs.h> +#include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -30,17 +32,14 @@ #include <media/v4l2-common.h> #include <media/v4l2-dev.h> -#include <media/videobuf-core.h> -#include <media/videobuf-dma-contig.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> #include <media/soc_camera.h> #include <media/soc_mediabus.h> #include <linux/videodev2.h> #include <mach/mx2_cam.h> -#ifdef CONFIG_MACH_MX27 -#include <mach/dma-mx1-mx2.h> -#endif #include <mach/hardware.h> #include <asm/dma.h> @@ -206,10 +205,23 @@ #define PRP_INTR_LBOVF (1 << 7) #define PRP_INTR_CH2OVF (1 << 8) -#define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma) +/* Resizing registers */ +#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) +#define PRP_RZ_VALID_BILINEAR (1 << 31) #define MAX_VIDEO_MEM 16 +#define RESIZE_NUM_MIN 1 +#define RESIZE_NUM_MAX 20 +#define BC_COEF 3 +#define SZ_COEF (1 << BC_COEF) + +#define RESIZE_DIR_H 0 +#define RESIZE_DIR_V 1 + +#define RESIZE_ALGO_BILINEAR 0 +#define RESIZE_ALGO_AVERAGING 1 + struct mx2_prp_cfg { int channel; u32 in_fmt; @@ -219,6 +231,13 @@ struct mx2_prp_cfg { u32 irq_flags; }; +/* prp resizing parameters */ +struct emma_prp_resize { + int algo; /* type of algorithm used */ + int len; /* number of coefficients */ + unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ +}; + /* prp configuration for a client-host fmt pair */ struct mx2_fmt_cfg { enum v4l2_mbus_pixelcode in_fmt; @@ -226,6 +245,26 @@ struct mx2_fmt_cfg { struct mx2_prp_cfg cfg; }; +enum mx2_buffer_state { + MX2_STATE_QUEUED, + MX2_STATE_ACTIVE, + MX2_STATE_DONE, +}; + +struct mx2_buf_internal { + struct list_head queue; + int bufnum; + bool discard; +}; + +/* buffer for one video frame */ +struct mx2_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_buffer vb; + enum mx2_buffer_state state; + struct mx2_buf_internal internal; +}; + struct mx2_camera_dev { struct device *dev; struct soc_camera_host soc_host; @@ -242,6 +281,7 @@ struct mx2_camera_dev { struct list_head capture; struct list_head active_bufs; + struct list_head discard; spinlock_t lock; @@ -250,26 +290,23 @@ struct mx2_camera_dev { struct mx2_buffer *fb1_active; struct mx2_buffer *fb2_active; - int use_emma; - u32 csicr1; + struct mx2_buf_internal buf_discard[2]; void *discard_buffer; dma_addr_t discard_buffer_dma; size_t discard_size; struct mx2_fmt_cfg *emma_prp; + struct emma_prp_resize resizing[2]; + unsigned int s_width, s_height; u32 frame_count; + struct vb2_alloc_ctx *alloc_ctx; }; -/* buffer for one video frame */ -struct mx2_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - enum v4l2_mbus_pixelcode code; - - int bufnum; -}; +static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) +{ + return container_of(int_buf, struct mx2_buffer, internal); +} static struct mx2_fmt_cfg mx27_emma_prp_table[] = { /* @@ -324,13 +361,36 @@ static struct mx2_fmt_cfg *mx27_emma_prp_get_format( return &mx27_emma_prp_table[0]; }; +static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, + unsigned long phys, int bufnum) +{ + struct mx2_fmt_cfg *prp = pcdev->emma_prp; + + if (prp->cfg.channel == 1) { + writel(phys, pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum); + } else { + writel(phys, pcdev->base_emma + + PRP_DEST_Y_PTR - 0x14 * bufnum); + if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { + u32 imgsize = pcdev->icd->user_height * + pcdev->icd->user_width; + + writel(phys + imgsize, pcdev->base_emma + + PRP_DEST_CB_PTR - 0x14 * bufnum); + writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + + PRP_DEST_CR_PTR - 0x14 * bufnum); + } + } +} + static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { unsigned long flags; clk_disable(pcdev->clk_csi); writel(0, pcdev->base_csi + CSICR1); - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { writel(0, pcdev->base_emma + PRP_CNTL); } else if (cpu_is_mx25()) { spin_lock_irqsave(&pcdev->lock, flags); @@ -362,7 +422,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) csicr1 = CSICR1_MCLKEN; - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | CSICR1_RXFF_LEVEL(0); } else if (cpu_is_mx27()) @@ -392,56 +452,13 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd) mx2_camera_deactivate(pcdev); - if (pcdev->discard_buffer) { - dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, - pcdev->discard_buffer, - pcdev->discard_buffer_dma); - pcdev->discard_buffer = NULL; - } - pcdev->icd = NULL; } -#ifdef CONFIG_MACH_MX27 -static void mx27_camera_dma_enable(struct mx2_camera_dev *pcdev) -{ - u32 tmp; - - imx_dma_enable(pcdev->dma); - - tmp = readl(pcdev->base_csi + CSICR1); - tmp |= CSICR1_RF_OR_INTEN; - writel(tmp, pcdev->base_csi + CSICR1); -} - -static irqreturn_t mx27_camera_irq(int irq_csi, void *data) -{ - struct mx2_camera_dev *pcdev = data; - u32 status = readl(pcdev->base_csi + CSISR); - - if (status & CSISR_SOF_INT && pcdev->active) { - u32 tmp; - - tmp = readl(pcdev->base_csi + CSICR1); - writel(tmp | CSICR1_CLR_RXFIFO, pcdev->base_csi + CSICR1); - mx27_camera_dma_enable(pcdev); - } - - writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR); - - return IRQ_HANDLED; -} -#else -static irqreturn_t mx27_camera_irq(int irq_csi, void *data) -{ - return IRQ_NONE; -} -#endif /* CONFIG_MACH_MX27 */ - static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, int state) { - struct videobuf_buffer *vb; + struct vb2_buffer *vb; struct mx2_buffer *buf; struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : &pcdev->fb2_active; @@ -454,25 +471,24 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, goto out; vb = &(*fb_active)->vb; - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count++; - - wake_up(&vb->done); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); if (list_empty(&pcdev->capture)) { buf = NULL; writel(0, pcdev->base_csi + fb_reg); } else { - buf = list_entry(pcdev->capture.next, struct mx2_buffer, - vb.queue); + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); vb = &buf->vb; - list_del(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; - writel(videobuf_to_dma_contig(vb), pcdev->base_csi + fb_reg); + list_del(&buf->internal.queue); + buf->state = MX2_STATE_ACTIVE; + writel(vb2_dma_contig_plane_dma_addr(vb, 0), + pcdev->base_csi + fb_reg); } *fb_active = buf; @@ -487,9 +503,9 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data) u32 status = readl(pcdev->base_csi + CSISR); if (status & CSISR_DMA_TSF_FB1_INT) - mx25_camera_frame_done(pcdev, 1, VIDEOBUF_DONE); + mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE); else if (status & CSISR_DMA_TSF_FB2_INT) - mx25_camera_frame_done(pcdev, 2, VIDEOBUF_DONE); + mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE); /* FIXME: handle CSISR_RFF_OR_INT */ @@ -501,59 +517,50 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data) /* * Videobuf operations */ -static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int mx2_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); + + /* TODO: support for VIDIOC_CREATE_BUFS not ready */ + if (fmt != NULL) + return -ENOTTY; if (bytes_per_line < 0) return bytes_per_line; - *size = bytes_per_line * icd->user_height; + alloc_ctxs[0] = pcdev->alloc_ctx; + + sizes[0] = bytes_per_line * icd->user_height; if (0 == *count) *count = 32; - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; + if (!*num_planes && + sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0]; - return 0; -} + *num_planes = 1; -static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf) -{ - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_buffer *vb = &buf->vb; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* - * This waits until this buffer is out of danger, i.e., until it is no - * longer in state VIDEOBUF_QUEUED or VIDEOBUF_ACTIVE - */ - videobuf_waiton(vq, vb, 0, 0); - - videobuf_dma_contig_free(vq, vb); - dev_dbg(icd->parent, "%s freed\n", __func__); - - vb->state = VIDEOBUF_NEEDS_INIT; + return 0; } -static int mx2_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) +static int mx2_videobuf_prepare(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); int ret = 0; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); if (bytes_per_line < 0) return bytes_per_line; @@ -563,99 +570,58 @@ static int mx2_videobuf_prepare(struct videobuf_queue *vq, * This can be useful if you want to see if we actually fill * the buffer with something */ - memset((void *)vb->baddr, 0xaa, vb->bsize); + memset((void *)vb2_plane_vaddr(vb, 0), + 0xaa, vb2_get_plane_payload(vb, 0)); #endif - if (buf->code != icd->current_fmt->code || - vb->width != icd->user_width || - vb->height != icd->user_height || - vb->field != field) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = bytes_per_line * vb->height; - if (vb->baddr && vb->bsize < vb->size) { + vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { ret = -EINVAL; goto out; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - vb->state = VIDEOBUF_PREPARED; - } - return 0; -fail: - free_buffer(vq, buf); out: return ret; } -static void mx2_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx2_videobuf_queue(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); spin_lock_irqsave(&pcdev->lock, flags); - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &pcdev->capture); + buf->state = MX2_STATE_QUEUED; + list_add_tail(&buf->internal.queue, &pcdev->capture); - if (mx27_camera_emma(pcdev)) { - goto out; -#ifdef CONFIG_MACH_MX27 - } else if (cpu_is_mx27()) { - int ret; - - if (pcdev->active == NULL) { - ret = imx_dma_setup_single(pcdev->dma, - videobuf_to_dma_contig(vb), vb->size, - (u32)pcdev->base_dma + 0x10, - DMA_MODE_READ); - if (ret) { - vb->state = VIDEOBUF_ERROR; - wake_up(&vb->done); - goto out; - } - - vb->state = VIDEOBUF_ACTIVE; - pcdev->active = buf; - } -#endif - } else { /* cpu_is_mx25() */ + if (cpu_is_mx25()) { u32 csicr3, dma_inten = 0; if (pcdev->fb1_active == NULL) { - writel(videobuf_to_dma_contig(vb), + writel(vb2_dma_contig_plane_dma_addr(vb, 0), pcdev->base_csi + CSIDMASA_FB1); pcdev->fb1_active = buf; dma_inten = CSICR1_FB1_DMA_INTEN; } else if (pcdev->fb2_active == NULL) { - writel(videobuf_to_dma_contig(vb), + writel(vb2_dma_contig_plane_dma_addr(vb, 0), pcdev->base_csi + CSIDMASA_FB2); pcdev->fb2_active = buf; dma_inten = CSICR1_FB2_DMA_INTEN; } if (dma_inten) { - list_del(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; + list_del(&buf->internal.queue); + buf->state = MX2_STATE_ACTIVE; csicr3 = readl(pcdev->base_csi + CSICR3); @@ -674,36 +640,31 @@ static void mx2_videobuf_queue(struct videobuf_queue *vq, } } -out: spin_unlock_irqrestore(&pcdev->lock, flags); } -static void mx2_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void mx2_videobuf_release(struct vb2_buffer *vb) { - struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); unsigned long flags; #ifdef DEBUG - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - switch (vb->state) { - case VIDEOBUF_ACTIVE: + switch (buf->state) { + case MX2_STATE_ACTIVE: dev_info(icd->parent, "%s (active)\n", __func__); break; - case VIDEOBUF_QUEUED: + case MX2_STATE_QUEUED: dev_info(icd->parent, "%s (queued)\n", __func__); break; - case VIDEOBUF_PREPARED: - dev_info(icd->parent, "%s (prepared)\n", __func__); - break; default: dev_info(icd->parent, "%s (unknown) %d\n", __func__, - vb->state); + buf->state); break; } #endif @@ -717,11 +678,9 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, * state. This requires a specific handling for each of the these DMA * types. */ + spin_lock_irqsave(&pcdev->lock, flags); - if (vb->state == VIDEOBUF_QUEUED) { - list_del(&vb->queue); - vb->state = VIDEOBUF_ERROR; - } else if (cpu_is_mx25() && vb->state == VIDEOBUF_ACTIVE) { + if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) { if (pcdev->fb1_active == buf) { pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN; writel(0, pcdev->base_csi + CSIDMASA_FB1); @@ -732,75 +691,178 @@ static void mx2_videobuf_release(struct videobuf_queue *vq, pcdev->fb2_active = NULL; } writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - vb->state = VIDEOBUF_ERROR; } spin_unlock_irqrestore(&pcdev->lock, flags); - - free_buffer(vq, buf); } -static struct videobuf_queue_ops mx2_videobuf_ops = { - .buf_setup = mx2_videobuf_setup, - .buf_prepare = mx2_videobuf_prepare, - .buf_queue = mx2_videobuf_queue, - .buf_release = mx2_videobuf_release, -}; - -static void mx2_camera_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) +static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, + int bytesperline) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct soc_camera_host *ici = + to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_fmt_cfg *prp = pcdev->emma_prp; - videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev, - &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, sizeof(struct mx2_buffer), - icd, &icd->video_lock); -} + writel((pcdev->s_width << 16) | pcdev->s_height, + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + writel(prp->cfg.src_pixel, + pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + if (prp->cfg.channel == 1) { + writel((icd->user_width << 16) | icd->user_height, + pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); + writel(bytesperline, + pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); + writel(prp->cfg.ch1_pixel, + pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); + } else { /* channel 2 */ + writel((icd->user_width << 16) | icd->user_height, + pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + } -#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH | \ - V4L2_MBUS_DATA_ACTIVE_LOW) + /* Enable interrupts */ + writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); +} -static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) { - u32 cntl; - int count = 0; + int dir; - cntl = readl(pcdev->base_emma + PRP_CNTL); - writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); - while (count++ < 100) { - if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) - return 0; - barrier(); - udelay(1); - } + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + unsigned char *s = pcdev->resizing[dir].s; + int len = pcdev->resizing[dir].len; + unsigned int coeff[2] = {0, 0}; + unsigned int valid = 0; + int i; - return -ETIMEDOUT; + if (len == 0) + continue; + + for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { + int j; + + j = i > 9 ? 1 : 0; + coeff[j] = (coeff[j] << BC_COEF) | + (s[i] & (SZ_COEF - 1)); + + if (i == 5 || i == 15) + coeff[j] <<= 1; + + valid = (valid << 1) | (s[i] >> BC_COEF); + } + + valid |= PRP_RZ_VALID_TBL_LEN(len); + + if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) + valid |= PRP_RZ_VALID_BILINEAR; + + if (pcdev->emma_prp->cfg.channel == 1) { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH1_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH1_RZ_VERT_VALID); + } + } else { + if (dir == RESIZE_DIR_H) { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_HORI_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_HORI_VALID); + } else { + writel(coeff[0], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF1); + writel(coeff[1], pcdev->base_emma + + PRP_CH2_RZ_VERT_COEF2); + writel(valid, pcdev->base_emma + + PRP_CH2_RZ_VERT_VALID); + } + } + } } -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, - int bytesperline) +static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) { + struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *prp = pcdev->emma_prp; - u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width; + struct vb2_buffer *vb; + struct mx2_buffer *buf; + unsigned long phys; + int bytesperline; - if (prp->cfg.channel == 1) { - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_RGB1_PTR); - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_RGB2_PTR); + if (cpu_is_mx27()) { + unsigned long flags; + if (count < 2) + return -EINVAL; + + spin_lock_irqsave(&pcdev->lock, flags); + + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 0; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; + + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 1; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - writel(PRP_CNTL_CH1EN | + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) + return bytesperline; + + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, &pcdev->discard_buffer_dma, + GFP_KERNEL); + if (!pcdev->discard_buffer) + return -ENOMEM; + + pcdev->buf_discard[0].discard = true; + list_add_tail(&pcdev->buf_discard[0].queue, + &pcdev->discard); + + pcdev->buf_discard[1].discard = true; + list_add_tail(&pcdev->buf_discard[1].queue, + &pcdev->discard); + + mx2_prp_resize_commit(pcdev); + + mx27_camera_emma_buf_init(icd, bytesperline); + + if (prp->cfg.channel == 1) { + writel(PRP_CNTL_CH1EN | PRP_CNTL_CSIEN | prp->cfg.in_fmt | prp->cfg.out_fmt | @@ -809,56 +871,107 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, PRP_CNTL_CH1_TSKIP(0) | PRP_CNTL_IN_TSKIP(0), pcdev->base_emma + PRP_CNTL); + } else { + writel(PRP_CNTL_CH2EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH2_LEN | + PRP_CNTL_CH2_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } + spin_unlock_irqrestore(&pcdev->lock, flags); + } - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); - writel(bytesperline, - pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); - writel(prp->cfg.ch1_pixel, - pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); - } else { /* channel 2 */ - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_DEST_Y_PTR); - writel(pcdev->discard_buffer_dma, - pcdev->base_emma + PRP_SOURCE_Y_PTR); - - if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) { - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_DEST_CB_PTR); - writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4), - pcdev->base_emma + PRP_DEST_CR_PTR); - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_SOURCE_CB_PTR); - writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4), - pcdev->base_emma + PRP_SOURCE_CR_PTR); + return 0; +} + +static int mx2_stop_streaming(struct vb2_queue *q) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(q); + struct soc_camera_host *ici = + to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; + struct mx2_fmt_cfg *prp = pcdev->emma_prp; + unsigned long flags; + void *b; + u32 cntl; + + if (cpu_is_mx27()) { + spin_lock_irqsave(&pcdev->lock, flags); + + cntl = readl(pcdev->base_emma + PRP_CNTL); + if (prp->cfg.channel == 1) { + writel(cntl & ~PRP_CNTL_CH1EN, + pcdev->base_emma + PRP_CNTL); + } else { + writel(cntl & ~PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); } + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); + b = pcdev->discard_buffer; + pcdev->discard_buffer = NULL; - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); + spin_unlock_irqrestore(&pcdev->lock, flags); - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, b, pcdev->discard_buffer_dma); + } - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); + return 0; +} + +static struct vb2_ops mx2_videobuf_ops = { + .queue_setup = mx2_videobuf_setup, + .buf_prepare = mx2_videobuf_prepare, + .buf_queue = mx2_videobuf_queue, + .buf_cleanup = mx2_videobuf_release, + .start_streaming = mx2_start_streaming, + .stop_streaming = mx2_stop_streaming, +}; + +static int mx2_camera_init_videobuf(struct vb2_queue *q, + struct soc_camera_device *icd) +{ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = icd; + q->ops = &mx2_videobuf_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct mx2_buffer); + + return vb2_queue_init(q); +} +#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH | \ + V4L2_MBUS_DATA_ACTIVE_LOW) + +static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) +{ + u32 cntl; + int count = 0; + + cntl = readl(pcdev->base_emma + PRP_CNTL); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + while (count++ < 100) { + if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) + return 0; + barrier(); + udelay(1); } - /* Enable interrupts */ - writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); + return -ETIMEDOUT; } static int mx2_camera_set_bus_param(struct soc_camera_device *icd) @@ -939,31 +1052,10 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) if (bytesperline < 0) return bytesperline; - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { ret = mx27_camera_emma_prp_reset(pcdev); if (ret) return ret; - - if (pcdev->discard_buffer) - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, pcdev->discard_buffer, - pcdev->discard_buffer_dma); - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_KERNEL); - if (!pcdev->discard_buffer) - return -ENOMEM; - - mx27_camera_emma_buf_init(icd, bytesperline); } else if (cpu_is_mx25()) { writel((bytesperline * icd->user_height) >> 2, pcdev->base_csi + CSIRXCNT); @@ -1052,6 +1144,123 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, return formats; } +static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, + struct v4l2_mbus_framefmt *mf_in, + struct v4l2_pix_format *pix_out, bool apply) +{ + int num, den; + unsigned long m; + int i, dir; + + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { + struct emma_prp_resize tmprsz; + unsigned char *s = tmprsz.s; + int len = 0; + int in, out; + + if (dir == RESIZE_DIR_H) { + in = mf_in->width; + out = pix_out->width; + } else { + in = mf_in->height; + out = pix_out->height; + } + + if (in < out) + return -EINVAL; + else if (in == out) + continue; + + /* Calculate ratio */ + m = gcd(in, out); + num = in / m; + den = out / m; + if (num > RESIZE_NUM_MAX) + return -EINVAL; + + if ((num >= 2 * den) && (den == 1) && + (num < 9) && (!(num & 0x01))) { + int sum = 0; + int j; + + /* Average scaling for >= 2:1 ratios */ + /* Support can be added for num >=9 and odd values */ + + tmprsz.algo = RESIZE_ALGO_AVERAGING; + len = num; + + for (i = 0; i < (len / 2); i++) + s[i] = 8; + + do { + for (i = 0; i < (len / 2); i++) { + s[i] = s[i] >> 1; + sum = 0; + for (j = 0; j < (len / 2); j++) + sum += s[j]; + if (sum == 4) + break; + } + } while (sum != 4); + + for (i = (len / 2); i < len; i++) + s[i] = s[len - i - 1]; + + s[len - 1] |= SZ_COEF; + } else { + /* bilinear scaling for < 2:1 ratios */ + int v; /* overflow counter */ + int coeff, nxt; /* table output */ + int in_pos_inc = 2 * den; + int out_pos = num; + int out_pos_inc = 2 * num; + int init_carry = num - den; + int carry = init_carry; + + tmprsz.algo = RESIZE_ALGO_BILINEAR; + v = den + in_pos_inc; + do { + coeff = v - out_pos; + out_pos += out_pos_inc; + carry += out_pos_inc; + for (nxt = 0; v < out_pos; nxt++) { + v += in_pos_inc; + carry -= in_pos_inc; + } + + if (len > RESIZE_NUM_MAX) + return -EINVAL; + + coeff = ((coeff << BC_COEF) + + (in_pos_inc >> 1)) / in_pos_inc; + + if (coeff >= (SZ_COEF - 1)) + coeff--; + + coeff |= SZ_COEF; + s[len] = (unsigned char)coeff; + len++; + + for (i = 1; i < nxt; i++) { + if (len >= RESIZE_NUM_MAX) + return -EINVAL; + s[len] = 0; + len++; + } + } while (carry != init_carry); + } + tmprsz.len = len; + if (dir == RESIZE_DIR_H) + mf_in->width = pix_out->width; + else + mf_in->height = pix_out->height; + + if (apply) + memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); + } + return 0; +} + static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -1063,6 +1272,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt mf; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { dev_warn(icd->parent, "Format %x not found\n", @@ -1080,6 +1292,22 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, if (ret < 0 && ret != -ENOIOCTLCMD) return ret; + /* Store width and height returned by the sensor for resizing */ + pcdev->s_width = mf.width; + pcdev->s_height = mf.height; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.code != xlate->code) return -EINVAL; @@ -1089,9 +1317,8 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, pix->colorspace = mf.colorspace; icd->current_fmt = xlate; - if (mx27_camera_emma(pcdev)) - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); return 0; } @@ -1104,9 +1331,14 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; __u32 pixfmt = pix->pixelformat; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx2_camera_dev *pcdev = ici->priv; unsigned int width_limit; int ret; + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { dev_warn(icd->parent, "Format %x not found\n", pixfmt); @@ -1156,6 +1388,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", + __func__, pcdev->s_width, pcdev->s_height); + + /* If the sensor does not support image size try PrP resizing */ + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, + xlate->host_fmt->fourcc); + + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); + if ((mf.width != pix->width || mf.height != pix->height) && + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { + if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0) + dev_dbg(icd->parent, "%s: can't resize\n", __func__); + } + if (mf.field == V4L2_FIELD_ANY) mf.field = V4L2_FIELD_NONE; /* @@ -1174,6 +1420,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->field = mf.field; pix->colorspace = mf.colorspace; + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", + __func__, pix->width, pix->height); + return 0; } @@ -1187,136 +1436,11 @@ static int mx2_camera_querycap(struct soc_camera_host *ici, return 0; } -static int mx2_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - for (i = 0; i < p->count; i++) { - struct mx2_buffer *buf = container_of(icd->vb_vidq.bufs[i], - struct mx2_buffer, vb); - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -#ifdef CONFIG_MACH_MX27 -static void mx27_camera_frame_done(struct mx2_camera_dev *pcdev, int state) -{ - struct videobuf_buffer *vb; - struct mx2_buffer *buf; - unsigned long flags; - int ret; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (!pcdev->active) { - dev_err(pcdev->dev, "%s called with no active buffer!\n", - __func__); - goto out; - } - - vb = &pcdev->active->vb; - buf = container_of(vb, struct mx2_buffer, vb); - WARN_ON(list_empty(&vb->queue)); - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count++; - - wake_up(&vb->done); - - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - goto out; - } - - pcdev->active = list_entry(pcdev->capture.next, - struct mx2_buffer, vb.queue); - - vb = &pcdev->active->vb; - vb->state = VIDEOBUF_ACTIVE; - - ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb), - vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ); - - if (ret) { - vb->state = VIDEOBUF_ERROR; - pcdev->active = NULL; - wake_up(&vb->done); - } - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx27_camera_dma_err_callback(int channel, void *data, int err) -{ - struct mx2_camera_dev *pcdev = data; - - mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR); -} - -static void mx27_camera_dma_callback(int channel, void *data) -{ - struct mx2_camera_dev *pcdev = data; - - mx27_camera_frame_done(pcdev, VIDEOBUF_DONE); -} - -#define DMA_REQ_CSI_RX 31 /* FIXME: Add this to a resource */ - -static int __devinit mx27_camera_dma_init(struct platform_device *pdev, - struct mx2_camera_dev *pcdev) -{ - int err; - - pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH); - if (pcdev->dma < 0) { - dev_err(&pdev->dev, "%s failed to request DMA channel\n", - __func__); - return pcdev->dma; - } - - err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback, - mx27_camera_dma_err_callback, pcdev); - if (err) { - dev_err(&pdev->dev, "%s failed to set DMA callback\n", - __func__); - goto err_out; - } - - err = imx_dma_config_channel(pcdev->dma, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, - DMA_REQ_CSI_RX, 1); - if (err) { - dev_err(&pdev->dev, "%s failed to config DMA channel\n", - __func__); - goto err_out; - } - - imx_dma_config_burstlen(pcdev->dma, 64); - - return 0; - -err_out: - imx_dma_free(pcdev->dma); - - return err; -} -#endif /* CONFIG_MACH_MX27 */ - static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; - return videobuf_poll_stream(file, &icd->vb_vidq, pt); + return vb2_poll(&icd->vb2_vidq, file, pt); } static struct soc_camera_host_ops mx2_soc_camera_host_ops = { @@ -1327,144 +1451,148 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = { .set_crop = mx2_camera_set_crop, .get_formats = mx2_camera_get_formats, .try_fmt = mx2_camera_try_fmt, - .init_videobuf = mx2_camera_init_videobuf, - .reqbufs = mx2_camera_reqbufs, + .init_videobuf2 = mx2_camera_init_videobuf, .poll = mx2_camera_poll, .querycap = mx2_camera_querycap, .set_bus_param = mx2_camera_set_bus_param, }; static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, - int bufnum, int state) + int bufnum, bool err) { - u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width; +#ifdef DEBUG struct mx2_fmt_cfg *prp = pcdev->emma_prp; +#endif + struct mx2_buf_internal *ibuf; struct mx2_buffer *buf; - struct videobuf_buffer *vb; + struct vb2_buffer *vb; unsigned long phys; - if (!list_empty(&pcdev->active_bufs)) { - buf = list_entry(pcdev->active_bufs.next, - struct mx2_buffer, vb.queue); + ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, + queue); + + BUG_ON(ibuf->bufnum != bufnum); - BUG_ON(buf->bufnum != bufnum); + if (ibuf->discard) { + /* + * Discard buffer must not be returned to user space. + * Just return it to the discard queue. + */ + list_move_tail(pcdev->active_bufs.next, &pcdev->discard); + } else { + buf = mx2_ibuf_to_buf(ibuf); vb = &buf->vb; #ifdef DEBUG - phys = videobuf_to_dma_contig(vb); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); if (prp->cfg.channel == 1) { if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum) != phys) { - dev_err(pcdev->dev, "%p != %p\n", phys, - readl(pcdev->base_emma + - PRP_DEST_RGB1_PTR + - 4 * bufnum)); + dev_err(pcdev->dev, "%lx != %x\n", phys, + readl(pcdev->base_emma + + PRP_DEST_RGB1_PTR + 4 * bufnum)); } } else { if (readl(pcdev->base_emma + PRP_DEST_Y_PTR - 0x14 * bufnum) != phys) { - dev_err(pcdev->dev, "%p != %p\n", phys, - readl(pcdev->base_emma + - PRP_DEST_Y_PTR - - 0x14 * bufnum)); + dev_err(pcdev->dev, "%lx != %x\n", phys, + readl(pcdev->base_emma + + PRP_DEST_Y_PTR - 0x14 * bufnum)); } } #endif - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, - vb->baddr, vb->bsize); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, + vb2_plane_vaddr(vb, 0), + vb2_get_plane_payload(vb, 0)); - list_del(&vb->queue); - vb->state = state; - do_gettimeofday(&vb->ts); - vb->field_count = pcdev->frame_count * 2; - pcdev->frame_count++; - - wake_up(&vb->done); + list_del_init(&buf->internal.queue); + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.sequence = pcdev->frame_count; + if (err) + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + else + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } + pcdev->frame_count++; + if (list_empty(&pcdev->capture)) { - if (prp->cfg.channel == 1) { - writel(pcdev->discard_buffer_dma, pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(pcdev->discard_buffer_dma, pcdev->base_emma + - PRP_DEST_Y_PTR - - 0x14 * bufnum); - if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - writel(pcdev->discard_buffer_dma + imgsize, - pcdev->base_emma + PRP_DEST_CB_PTR - - 0x14 * bufnum); - writel(pcdev->discard_buffer_dma + - ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } + if (list_empty(&pcdev->discard)) { + dev_warn(pcdev->dev, "%s: trying to access empty discard list\n", + __func__); + return; } + + ibuf = list_first_entry(&pcdev->discard, + struct mx2_buf_internal, queue); + ibuf->bufnum = bufnum; + + list_move_tail(pcdev->discard.next, &pcdev->active_bufs); + mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum); return; } - buf = list_entry(pcdev->capture.next, - struct mx2_buffer, vb.queue); + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); - buf->bufnum = !bufnum; + buf->internal.bufnum = bufnum; list_move_tail(pcdev->capture.next, &pcdev->active_bufs); vb = &buf->vb; - vb->state = VIDEOBUF_ACTIVE; + buf->state = MX2_STATE_ACTIVE; - phys = videobuf_to_dma_contig(vb); - if (prp->cfg.channel == 1) { - writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(phys, pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum); - if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) { - writel(phys + imgsize, pcdev->base_emma + - PRP_DEST_CB_PTR - 0x14 * bufnum); - writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } - } + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, bufnum); } static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) { struct mx2_camera_dev *pcdev = data; unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); - struct mx2_buffer *buf; + struct mx2_buf_internal *ibuf; + + spin_lock(&pcdev->lock); + + if (list_empty(&pcdev->active_bufs)) { + dev_warn(pcdev->dev, "%s: called while active list is empty\n", + __func__); + + if (!status) { + spin_unlock(&pcdev->lock); + return IRQ_NONE; + } + } if (status & (1 << 7)) { /* overflow */ - u32 cntl; - /* - * We only disable channel 1 here since this is the only - * enabled channel - * - * FIXME: the correct DMA overflow handling should be resetting - * the buffer, returning an error frame, and continuing with - * the next one. - */ - cntl = readl(pcdev->base_emma + PRP_CNTL); + u32 cntl = readl(pcdev->base_emma + PRP_CNTL); writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN), pcdev->base_emma + PRP_CNTL); writel(cntl, pcdev->base_emma + PRP_CNTL); - } - if ((((status & (3 << 5)) == (3 << 5)) || - ((status & (3 << 3)) == (3 << 3))) - && !list_empty(&pcdev->active_bufs)) { + + ibuf = list_first_entry(&pcdev->active_bufs, + struct mx2_buf_internal, queue); + mx27_camera_frame_done_emma(pcdev, + ibuf->bufnum, true); + + status &= ~(1 << 7); + } else if (((status & (3 << 5)) == (3 << 5)) || + ((status & (3 << 3)) == (3 << 3))) { /* * Both buffers have triggered, process the one we're expecting * to first */ - buf = list_entry(pcdev->active_bufs.next, - struct mx2_buffer, vb.queue); - mx27_camera_frame_done_emma(pcdev, buf->bufnum, VIDEOBUF_DONE); - status &= ~(1 << (6 - buf->bufnum)); /* mark processed */ + ibuf = list_first_entry(&pcdev->active_bufs, + struct mx2_buf_internal, queue); + mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false); + status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */ + } else if ((status & (1 << 6)) || (status & (1 << 4))) { + mx27_camera_frame_done_emma(pcdev, 0, false); + } else if ((status & (1 << 5)) || (status & (1 << 3))) { + mx27_camera_frame_done_emma(pcdev, 1, false); } - if ((status & (1 << 6)) || (status & (1 << 4))) - mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE); - if ((status & (1 << 5)) || (status & (1 << 3))) - mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE); + spin_unlock(&pcdev->lock); writel(status, pcdev->base_emma + PRP_INTRSTATUS); return IRQ_HANDLED; @@ -1527,8 +1655,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) struct resource *res_csi, *res_emma; void __iomem *base_csi; int irq_csi, irq_emma; - irq_handler_t mx2_cam_irq_handler = cpu_is_mx25() ? mx25_camera_irq - : mx27_camera_irq; int err = 0; dev_dbg(&pdev->dev, "initialising\n"); @@ -1550,22 +1676,11 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->clk_csi = clk_get(&pdev->dev, NULL); if (IS_ERR(pcdev->clk_csi)) { + dev_err(&pdev->dev, "Could not get csi clock\n"); err = PTR_ERR(pcdev->clk_csi); goto exit_kfree; } - dev_dbg(&pdev->dev, "Camera clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi)); - - /* Initialize DMA */ -#ifdef CONFIG_MACH_MX27 - if (cpu_is_mx27()) { - err = mx27_camera_dma_init(pdev, pcdev); - if (err) - goto exit_clk_put; - } -#endif /* CONFIG_MACH_MX27 */ - pcdev->res_csi = res_csi; pcdev->pdata = pdev->dev.platform_data; if (pcdev->pdata) { @@ -1585,6 +1700,7 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->capture); INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); spin_lock_init(&pcdev->lock); /* @@ -1606,11 +1722,13 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->base_dma = res_csi->start; pcdev->dev = &pdev->dev; - err = request_irq(pcdev->irq_csi, mx2_cam_irq_handler, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); - goto exit_iounmap; + if (cpu_is_mx25()) { + err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0, + MX2_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_iounmap; + } } if (cpu_is_mx27()) { @@ -1618,14 +1736,15 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); irq_emma = platform_get_irq(pdev, 1); - if (res_emma && irq_emma >= 0) { - dev_info(&pdev->dev, "Using EMMA\n"); - pcdev->use_emma = 1; - pcdev->res_emma = res_emma; - pcdev->irq_emma = irq_emma; - if (mx27_camera_emma_init(pcdev)) - goto exit_free_irq; + if (!res_emma || !irq_emma) { + dev_err(&pdev->dev, "no EMMA resources\n"); + goto exit_free_irq; } + + pcdev->res_emma = res_emma; + pcdev->irq_emma = irq_emma; + if (mx27_camera_emma_init(pcdev)) + goto exit_free_irq; } pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, @@ -1633,6 +1752,12 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; + + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + err = PTR_ERR(pcdev->alloc_ctx); + goto eallocctx; + } err = soc_camera_host_register(&pcdev->soc_host); if (err) goto exit_free_emma; @@ -1643,26 +1768,24 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) return 0; exit_free_emma: - if (mx27_camera_emma(pcdev)) { + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); +eallocctx: + if (cpu_is_mx27()) { free_irq(pcdev->irq_emma, pcdev); clk_disable(pcdev->clk_emma); clk_put(pcdev->clk_emma); iounmap(pcdev->base_emma); - release_mem_region(res_emma->start, resource_size(res_emma)); + release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma)); } exit_free_irq: - free_irq(pcdev->irq_csi, pcdev); + if (cpu_is_mx25()) + free_irq(pcdev->irq_csi, pcdev); exit_iounmap: iounmap(base_csi); exit_release: release_mem_region(res_csi->start, resource_size(res_csi)); exit_dma_free: -#ifdef CONFIG_MACH_MX27 - if (cpu_is_mx27()) - imx_dma_free(pcdev->dma); -exit_clk_put: clk_put(pcdev->clk_csi); -#endif /* CONFIG_MACH_MX27 */ exit_kfree: kfree(pcdev); exit: @@ -1677,19 +1800,18 @@ static int __devexit mx2_camera_remove(struct platform_device *pdev) struct resource *res; clk_put(pcdev->clk_csi); -#ifdef CONFIG_MACH_MX27 + if (cpu_is_mx25()) + free_irq(pcdev->irq_csi, pcdev); if (cpu_is_mx27()) - imx_dma_free(pcdev->dma); -#endif /* CONFIG_MACH_MX27 */ - free_irq(pcdev->irq_csi, pcdev); - if (mx27_camera_emma(pcdev)) free_irq(pcdev->irq_emma, pcdev); soc_camera_host_unregister(&pcdev->soc_host); + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); + iounmap(pcdev->base_csi); - if (mx27_camera_emma(pcdev)) { + if (cpu_is_mx27()) { clk_disable(pcdev->clk_emma); clk_put(pcdev->clk_emma); iounmap(pcdev->base_emma); diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c new file mode 100644 index 0000000..ba89a74 --- /dev/null +++ b/drivers/media/video/mx2_emmaprp.c @@ -0,0 +1,1008 @@ +/* + * Support eMMa-PrP through mem2mem framework. + * + * eMMa-PrP is a piece of HW that allows fetching buffers + * from one memory location and do several operations on + * them such as scaling or format conversion giving, as a result + * a new processed buffer in another memory location. + * + * Based on mem2mem_testdev.c by Pawel Osciak. + * + * Copyright (c) 2011 Vista Silicon S.L. + * Javier Martin <javier.martin@vista-silicon.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/clk.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <linux/platform_device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> +#include <asm/sizes.h> + +#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp" + +MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs"); +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.1"); + +static bool debug; +module_param(debug, bool, 0644); + +#define MIN_W 32 +#define MIN_H 32 +#define MAX_W 2040 +#define MAX_H 2046 + +#define S_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN_YUV420 3 /* multiple of 8 */ +#define W_ALIGN_OTHERS 2 /* multiple of 4 */ +#define H_ALIGN 1 /* multiple of 2 */ + +/* 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-emmaprp" + +/* In bytes, per queue */ +#define MEM2MEM_VID_MEM_LIMIT SZ_16M + +#define dprintk(dev, fmt, arg...) \ + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + +/* EMMA PrP */ +#define PRP_CNTL 0x00 +#define PRP_INTR_CNTL 0x04 +#define PRP_INTRSTATUS 0x08 +#define PRP_SOURCE_Y_PTR 0x0c +#define PRP_SOURCE_CB_PTR 0x10 +#define PRP_SOURCE_CR_PTR 0x14 +#define PRP_DEST_RGB1_PTR 0x18 +#define PRP_DEST_RGB2_PTR 0x1c +#define PRP_DEST_Y_PTR 0x20 +#define PRP_DEST_CB_PTR 0x24 +#define PRP_DEST_CR_PTR 0x28 +#define PRP_SRC_FRAME_SIZE 0x2c +#define PRP_DEST_CH1_LINE_STRIDE 0x30 +#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 +#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 +#define PRP_CH1_OUT_IMAGE_SIZE 0x3c +#define PRP_CH2_OUT_IMAGE_SIZE 0x40 +#define PRP_SRC_LINE_STRIDE 0x44 +#define PRP_CSC_COEF_012 0x48 +#define PRP_CSC_COEF_345 0x4c +#define PRP_CSC_COEF_678 0x50 +#define PRP_CH1_RZ_HORI_COEF1 0x54 +#define PRP_CH1_RZ_HORI_COEF2 0x58 +#define PRP_CH1_RZ_HORI_VALID 0x5c +#define PRP_CH1_RZ_VERT_COEF1 0x60 +#define PRP_CH1_RZ_VERT_COEF2 0x64 +#define PRP_CH1_RZ_VERT_VALID 0x68 +#define PRP_CH2_RZ_HORI_COEF1 0x6c +#define PRP_CH2_RZ_HORI_COEF2 0x70 +#define PRP_CH2_RZ_HORI_VALID 0x74 +#define PRP_CH2_RZ_VERT_COEF1 0x78 +#define PRP_CH2_RZ_VERT_COEF2 0x7c +#define PRP_CH2_RZ_VERT_VALID 0x80 + +#define PRP_CNTL_CH1EN (1 << 0) +#define PRP_CNTL_CH2EN (1 << 1) +#define PRP_CNTL_CSIEN (1 << 2) +#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) +#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) +#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) +#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) +#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) +#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) +#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) +#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) +#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) +#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) +#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) +#define PRP_CNTL_CH1_LEN (1 << 9) +#define PRP_CNTL_CH2_LEN (1 << 10) +#define PRP_CNTL_SKIP_FRAME (1 << 11) +#define PRP_CNTL_SWRST (1 << 12) +#define PRP_CNTL_CLKEN (1 << 13) +#define PRP_CNTL_WEN (1 << 14) +#define PRP_CNTL_CH1BYP (1 << 15) +#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) +#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) +#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) +#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) +#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) +#define PRP_CNTL_CH2B1EN (1 << 29) +#define PRP_CNTL_CH2B2EN (1 << 30) +#define PRP_CNTL_CH2FEN (1 << 31) + +#define PRP_SIZE_HEIGHT(x) (x) +#define PRP_SIZE_WIDTH(x) ((x) << 16) + +/* IRQ Enable and status register */ +#define PRP_INTR_RDERR (1 << 0) +#define PRP_INTR_CH1WERR (1 << 1) +#define PRP_INTR_CH2WERR (1 << 2) +#define PRP_INTR_CH1FC (1 << 3) +#define PRP_INTR_CH2FC (1 << 5) +#define PRP_INTR_LBOVF (1 << 7) +#define PRP_INTR_CH2OVF (1 << 8) + +#define PRP_INTR_ST_RDERR (1 << 0) +#define PRP_INTR_ST_CH1WERR (1 << 1) +#define PRP_INTR_ST_CH2WERR (1 << 2) +#define PRP_INTR_ST_CH2B2CI (1 << 3) +#define PRP_INTR_ST_CH2B1CI (1 << 4) +#define PRP_INTR_ST_CH1B2CI (1 << 5) +#define PRP_INTR_ST_CH1B1CI (1 << 6) +#define PRP_INTR_ST_LBOVF (1 << 7) +#define PRP_INTR_ST_CH2OVF (1 << 8) + +struct emmaprp_fmt { + char *name; + u32 fourcc; + /* Types the format can be used for */ + u32 types; +}; + +static struct emmaprp_fmt formats[] = { + { + .name = "YUV 4:2:0 Planar", + .fourcc = V4L2_PIX_FMT_YUV420, + .types = MEM2MEM_CAPTURE, + }, + { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .types = MEM2MEM_OUTPUT, + }, +}; + +/* Per-queue, driver-specific private data */ +struct emmaprp_q_data { + unsigned int width; + unsigned int height; + unsigned int sizeimage; + struct emmaprp_fmt *fmt; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static struct emmaprp_fmt *find_format(struct v4l2_format *f) +{ + struct emmaprp_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 emmaprp_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd; + + struct mutex dev_mutex; + spinlock_t irqlock; + + int irq_emma; + void __iomem *base_emma; + struct clk *clk_emma; + struct resource *res_emma; + + struct v4l2_m2m_dev *m2m_dev; + struct vb2_alloc_ctx *alloc_ctx; +}; + +struct emmaprp_ctx { + struct emmaprp_dev *dev; + /* Abort requested by m2m */ + int aborting; + struct emmaprp_q_data q_data[2]; + struct v4l2_m2m_ctx *m2m_ctx; +}; + +static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &(ctx->q_data[V4L2_M2M_SRC]); + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &(ctx->q_data[V4L2_M2M_DST]); + default: + BUG(); + } + return NULL; +} + +/* + * mem2mem callbacks + */ +static void emmaprp_job_abort(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_dev *pcdev = ctx->dev; + + ctx->aborting = 1; + + dprintk(pcdev, "Aborting task\n"); + + v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); +} + +static void emmaprp_lock(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_dev *pcdev = ctx->dev; + mutex_lock(&pcdev->dev_mutex); +} + +static void emmaprp_unlock(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_dev *pcdev = ctx->dev; + mutex_unlock(&pcdev->dev_mutex); +} + +static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev) +{ + dprintk(pcdev, + "eMMa-PrP Registers:\n" + " SOURCE_Y_PTR = 0x%08X\n" + " SRC_FRAME_SIZE = 0x%08X\n" + " DEST_Y_PTR = 0x%08X\n" + " DEST_CR_PTR = 0x%08X\n" + " DEST_CB_PTR = 0x%08X\n" + " CH2_OUT_IMAGE_SIZE = 0x%08X\n" + " CNTL = 0x%08X\n", + readl(pcdev->base_emma + PRP_SOURCE_Y_PTR), + readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE), + readl(pcdev->base_emma + PRP_DEST_Y_PTR), + readl(pcdev->base_emma + PRP_DEST_CR_PTR), + readl(pcdev->base_emma + PRP_DEST_CB_PTR), + readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE), + readl(pcdev->base_emma + PRP_CNTL)); +} + +static void emmaprp_device_run(void *priv) +{ + struct emmaprp_ctx *ctx = priv; + struct emmaprp_q_data *s_q_data, *d_q_data; + struct vb2_buffer *src_buf, *dst_buf; + struct emmaprp_dev *pcdev = ctx->dev; + unsigned int s_width, s_height; + unsigned int d_width, d_height; + unsigned int d_size; + dma_addr_t p_in, p_out; + u32 tmp; + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + + s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + s_width = s_q_data->width; + s_height = s_q_data->height; + + d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + d_width = d_q_data->width; + d_height = d_q_data->height; + d_size = d_width * d_height; + + p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0); + p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + if (!p_in || !p_out) { + v4l2_err(&pcdev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return; + } + + /* Input frame parameters */ + writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR); + writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height), + pcdev->base_emma + PRP_SRC_FRAME_SIZE); + + /* Output frame parameters */ + writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR); + writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR); + writel(p_out + d_size + (d_size >> 2), + pcdev->base_emma + PRP_DEST_CR_PTR); + writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height), + pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); + + /* IRQ configuration */ + tmp = readl(pcdev->base_emma + PRP_INTR_CNTL); + writel(tmp | PRP_INTR_RDERR | + PRP_INTR_CH2WERR | + PRP_INTR_CH2FC, + pcdev->base_emma + PRP_INTR_CNTL); + + emmaprp_dump_regs(pcdev); + + /* Enable transfer */ + tmp = readl(pcdev->base_emma + PRP_CNTL); + writel(tmp | PRP_CNTL_CH2_OUT_YUV420 | + PRP_CNTL_DATA_IN_YUV422 | + PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); +} + +static irqreturn_t emmaprp_irq(int irq_emma, void *data) +{ + struct emmaprp_dev *pcdev = data; + struct emmaprp_ctx *curr_ctx; + struct vb2_buffer *src_vb, *dst_vb; + unsigned long flags; + u32 irqst; + + /* Check irq flags and clear irq */ + irqst = readl(pcdev->base_emma + PRP_INTRSTATUS); + writel(irqst, pcdev->base_emma + PRP_INTRSTATUS); + dprintk(pcdev, "irqst = 0x%08x\n", irqst); + + curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev); + if (curr_ctx == NULL) { + pr_err("Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + if (!curr_ctx->aborting) { + if ((irqst & PRP_INTR_ST_RDERR) || + (irqst & PRP_INTR_ST_CH2WERR)) { + pr_err("PrP bus error ocurred, this transfer is probably corrupted\n"); + writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); + } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */ + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + + spin_lock_irqsave(&pcdev->irqlock, flags); + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); + spin_unlock_irqrestore(&pcdev->irqlock, flags); + } + } + + v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx); + return IRQ_HANDLED; +} + +/* + * 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->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 emmaprp_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]; + strlcpy(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 emmaprp_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct emmaprp_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = q_data->fmt->fourcc; + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + f->fmt.pix.bytesperline = q_data->width * 3 / 2; + else /* YUYV */ + f->fmt.pix.bytesperline = q_data->width * 2; + 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) +{ + enum v4l2_field field; + + + if (!find_format(f)) + return -EINVAL; + + 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.pixelformat == V4L2_PIX_FMT_YUV420) { + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, + W_ALIGN_YUV420, &f->fmt.pix.height, + MIN_H, MAX_H, H_ALIGN, S_ALIGN); + f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2; + } else { + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, + W_ALIGN_OTHERS, &f->fmt.pix.height, + MIN_H, MAX_H, H_ALIGN, S_ALIGN); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + } + 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 emmaprp_fmt *fmt; + struct emmaprp_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); +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct emmaprp_fmt *fmt; + struct emmaprp_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); +} + +static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) +{ + struct emmaprp_q_data *q_data; + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret = vidioc_try_fmt(f); + if (ret) + return ret; + + q_data->fmt = find_format(f); + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420) + q_data->sizeimage = q_data->width * q_data->height * 3 / 2; + else /* YUYV */ + q_data->sizeimage = q_data->width * q_data->height * 2; + + 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); + + return 0; +} + +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 emmaprp_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 emmaprp_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 emmaprp_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 emmaprp_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 emmaprp_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 emmaprp_ctx *ctx = priv; + + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops emmaprp_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, +}; + + +/* + * Queue operations + */ +static int emmaprp_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq); + struct emmaprp_q_data *q_data; + unsigned int size, count = *nbuffers; + + q_data = get_q_data(ctx, vq->type); + + if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420) + size = q_data->width * q_data->height * 3 / 2; + else + size = q_data->width * q_data->height * 2; + + while (size * count > MEM2MEM_VID_MEM_LIMIT) + (count)--; + + *nplanes = 1; + *nbuffers = count; + sizes[0] = size; + + alloc_ctxs[0] = ctx->dev->alloc_ctx; + + dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); + + return 0; +} + +static int emmaprp_buf_prepare(struct vb2_buffer *vb) +{ + struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct emmaprp_q_data *q_data; + + dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + dprintk(ctx->dev, "%s data will not fit into plane" + "(%lu < %lu)\n", __func__, + vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, q_data->sizeimage); + + return 0; +} + +static void emmaprp_buf_queue(struct vb2_buffer *vb) +{ + struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +} + +static struct vb2_ops emmaprp_qops = { + .queue_setup = emmaprp_queue_setup, + .buf_prepare = emmaprp_buf_prepare, + .buf_queue = emmaprp_buf_queue, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct emmaprp_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &emmaprp_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &emmaprp_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return vb2_queue_init(dst_vq); +} + +/* + * File operations + */ +static int emmaprp_open(struct file *file) +{ + struct emmaprp_dev *pcdev = video_drvdata(file); + struct emmaprp_ctx *ctx; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + file->private_data = ctx; + ctx->dev = pcdev; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); + + if (IS_ERR(ctx->m2m_ctx)) { + int ret = PTR_ERR(ctx->m2m_ctx); + + kfree(ctx); + return ret; + } + + clk_enable(pcdev->clk_emma); + ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1]; + ctx->q_data[V4L2_M2M_DST].fmt = &formats[0]; + + dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); + + return 0; +} + +static int emmaprp_release(struct file *file) +{ + struct emmaprp_dev *pcdev = video_drvdata(file); + struct emmaprp_ctx *ctx = file->private_data; + + dprintk(pcdev, "Releasing instance %p\n", ctx); + + clk_disable(pcdev->clk_emma); + v4l2_m2m_ctx_release(ctx->m2m_ctx); + kfree(ctx); + + return 0; +} + +static unsigned int emmaprp_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct emmaprp_ctx *ctx = file->private_data; + + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +} + +static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct emmaprp_ctx *ctx = file->private_data; + + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations emmaprp_fops = { + .owner = THIS_MODULE, + .open = emmaprp_open, + .release = emmaprp_release, + .poll = emmaprp_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = emmaprp_mmap, +}; + +static struct video_device emmaprp_videodev = { + .name = MEM2MEM_NAME, + .fops = &emmaprp_fops, + .ioctl_ops = &emmaprp_ioctl_ops, + .minor = -1, + .release = video_device_release, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = emmaprp_device_run, + .job_abort = emmaprp_job_abort, + .lock = emmaprp_lock, + .unlock = emmaprp_unlock, +}; + +static int emmaprp_probe(struct platform_device *pdev) +{ + struct emmaprp_dev *pcdev; + struct video_device *vfd; + struct resource *res_emma; + int irq_emma; + int ret; + + pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL); + if (!pcdev) + return -ENOMEM; + + spin_lock_init(&pcdev->irqlock); + + pcdev->clk_emma = clk_get(&pdev->dev, NULL); + if (IS_ERR(pcdev->clk_emma)) { + ret = PTR_ERR(pcdev->clk_emma); + goto free_dev; + } + + irq_emma = platform_get_irq(pdev, 0); + res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (irq_emma < 0 || res_emma == NULL) { + dev_err(&pdev->dev, "Missing platform resources data\n"); + ret = -ENODEV; + goto free_clk; + } + + ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); + if (ret) + goto free_clk; + + mutex_init(&pcdev->dev_mutex); + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto unreg_dev; + } + + *vfd = emmaprp_videodev; + vfd->lock = &pcdev->dev_mutex; + + video_set_drvdata(vfd, pcdev); + snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name); + pcdev->vfd = vfd; + v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME + " Device registered as /dev/video%d\n", vfd->num); + + platform_set_drvdata(pdev, pcdev); + + if (devm_request_mem_region(&pdev->dev, res_emma->start, + resource_size(res_emma), MEM2MEM_NAME) == NULL) + goto rel_vdev; + + pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start, + resource_size(res_emma)); + if (!pcdev->base_emma) + goto rel_vdev; + + pcdev->irq_emma = irq_emma; + pcdev->res_emma = res_emma; + + if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq, + 0, MEM2MEM_NAME, pcdev) < 0) + goto rel_vdev; + + pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pcdev->alloc_ctx)) { + v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); + ret = PTR_ERR(pcdev->alloc_ctx); + goto rel_vdev; + } + + pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(pcdev->m2m_dev)) { + v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(pcdev->m2m_dev); + goto rel_ctx; + } + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); + goto rel_m2m; + } + + return 0; + + +rel_m2m: + v4l2_m2m_release(pcdev->m2m_dev); +rel_ctx: + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); +rel_vdev: + video_device_release(vfd); +unreg_dev: + v4l2_device_unregister(&pcdev->v4l2_dev); +free_clk: + clk_put(pcdev->clk_emma); +free_dev: + kfree(pcdev); + + return ret; +} + +static int emmaprp_remove(struct platform_device *pdev) +{ + struct emmaprp_dev *pcdev = platform_get_drvdata(pdev); + + v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME); + + video_unregister_device(pcdev->vfd); + v4l2_m2m_release(pcdev->m2m_dev); + vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); + v4l2_device_unregister(&pcdev->v4l2_dev); + clk_put(pcdev->clk_emma); + kfree(pcdev); + + return 0; +} + +static struct platform_driver emmaprp_pdrv = { + .probe = emmaprp_probe, + .remove = emmaprp_remove, + .driver = { + .name = MEM2MEM_NAME, + .owner = THIS_MODULE, + }, +}; + +static void __exit emmaprp_exit(void) +{ + platform_driver_unregister(&emmaprp_pdrv); +} + +static int __init emmaprp_init(void) +{ + return platform_driver_register(&emmaprp_pdrv); +} + +module_init(emmaprp_init); +module_exit(emmaprp_exit); diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 7452277..93c35ef 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -286,7 +286,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0); sg_dma_len(sg) = new_size; - txd = ichan->dma_chan.device->device_prep_slave_sg( + txd = dmaengine_prep_slave_sg( &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!txd) diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c index 50838bf..440c129 100644 --- a/drivers/media/video/noon010pc30.c +++ b/drivers/media/video/noon010pc30.c @@ -725,8 +725,8 @@ static int noon010_probe(struct i2c_client *client, mutex_init(&info->lock); sd = &info->sd; - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &noon010_ops); + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); sd->internal_ops = &noon010_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -844,18 +844,7 @@ static struct i2c_driver noon010_i2c_driver = { .id_table = noon010_id, }; -static int __init noon010_init(void) -{ - return i2c_add_driver(&noon010_i2c_driver); -} - -static void __exit noon010_exit(void) -{ - i2c_del_driver(&noon010_i2c_driver); -} - -module_init(noon010_init); -module_exit(noon010_exit); +module_i2c_driver(noon010_i2c_driver); MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c index 1fb7d5b..88cf9d9 100644 --- a/drivers/media/video/omap/omap_vout.c +++ b/drivers/media/video/omap/omap_vout.c @@ -2268,13 +2268,12 @@ 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) { + if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) { printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); return -EINVAL; } diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c index b5247cb..3c2c5d3 100644 --- a/drivers/media/video/ov2640.c +++ b/drivers/media/video/ov2640.c @@ -1103,21 +1103,7 @@ static struct i2c_driver ov2640_i2c_driver = { .id_table = ov2640_id, }; -/* - * Module functions - */ -static int __init ov2640_module_init(void) -{ - return i2c_add_driver(&ov2640_i2c_driver); -} - -static void __exit ov2640_module_exit(void) -{ - i2c_del_driver(&ov2640_i2c_driver); -} - -module_init(ov2640_module_init); -module_exit(ov2640_module_exit); +module_i2c_driver(ov2640_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor"); MODULE_AUTHOR("Alberto Panizzo"); diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index bb37ec8..80e0779 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -1068,18 +1068,7 @@ static struct i2c_driver ov5642_i2c_driver = { .id_table = ov5642_id, }; -static int __init ov5642_mod_init(void) -{ - return i2c_add_driver(&ov5642_i2c_driver); -} - -static void __exit ov5642_mod_exit(void) -{ - i2c_del_driver(&ov5642_i2c_driver); -} - -module_init(ov5642_mod_init); -module_exit(ov5642_mod_exit); +module_i2c_driver(ov5642_i2c_driver); MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c index 6806345..3e028b1 100644 --- a/drivers/media/video/ov6650.c +++ b/drivers/media/video/ov6650.c @@ -649,7 +649,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) clkrc = CLKRC_24MHz; } else { dev_err(&client->dev, - "unspported input clock, check platform data\n"); + "unsupported input clock, check platform data\n"); return -EINVAL; } mclk = sense->master_clock; @@ -1046,18 +1046,7 @@ static struct i2c_driver ov6650_i2c_driver = { .id_table = ov6650_id, }; -static int __init ov6650_module_init(void) -{ - return i2c_add_driver(&ov6650_i2c_driver); -} - -static void __exit ov6650_module_exit(void) -{ - i2c_del_driver(&ov6650_i2c_driver); -} - -module_init(ov6650_module_init); -module_exit(ov6650_module_exit); +module_i2c_driver(ov6650_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650"); MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 6a56496..e7c82b2 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -1583,15 +1583,4 @@ static struct i2c_driver ov7670_driver = { .id_table = ov7670_id, }; -static __init int init_ov7670(void) -{ - return i2c_add_driver(&ov7670_driver); -} - -static __exit void exit_ov7670(void) -{ - i2c_del_driver(&ov7670_driver); -} - -module_init(init_ov7670); -module_exit(exit_ov7670); +module_i2c_driver(ov7670_driver); diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 9f6ce3d..74e77d3 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -1123,22 +1123,7 @@ static struct i2c_driver ov772x_i2c_driver = { .id_table = ov772x_id, }; -/* - * module function - */ - -static int __init ov772x_module_init(void) -{ - return i2c_add_driver(&ov772x_i2c_driver); -} - -static void __exit ov772x_module_exit(void) -{ - i2c_del_driver(&ov772x_i2c_driver); -} - -module_init(ov772x_module_init); -module_exit(ov772x_module_exit); +module_i2c_driver(ov772x_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for ov772x"); MODULE_AUTHOR("Kuninori Morimoto"); diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index a4f9979..23412de 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -738,18 +738,7 @@ static struct i2c_driver ov9640_i2c_driver = { .id_table = ov9640_id, }; -static int __init ov9640_module_init(void) -{ - return i2c_add_driver(&ov9640_i2c_driver); -} - -static void __exit ov9640_module_exit(void) -{ - i2c_del_driver(&ov9640_i2c_driver); -} - -module_init(ov9640_module_init); -module_exit(ov9640_module_exit); +module_i2c_driver(ov9640_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c index d9a9f71..3eb07c2 100644 --- a/drivers/media/video/ov9740.c +++ b/drivers/media/video/ov9740.c @@ -998,18 +998,7 @@ static struct i2c_driver ov9740_i2c_driver = { .id_table = ov9740_id, }; -static int __init ov9740_module_init(void) -{ - return i2c_add_driver(&ov9740_i2c_driver); -} - -static void __exit ov9740_module_exit(void) -{ - i2c_del_driver(&ov9740_i2c_driver); -} - -module_init(ov9740_module_init); -module_exit(ov9740_module_exit); +module_i2c_driver(ov9740_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index c6da8f7..d8c8982 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -320,7 +320,17 @@ static struct tda829x_config tda829x_no_probe = { .probe_tuner = TDA829X_DONT_PROBE, }; +static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + static struct tda18271_config hauppauge_tda18271_dvb_config = { + .std_map = &hauppauge_tda18271_dvbt_std_map, .gate = TDA18271_GATE_ANALOG, .output_opt = TDA18271_OUTPUT_LT_OFF, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 6d66617..e1111d9 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -96,7 +96,6 @@ static struct v4l2_capability pvr_capability ={ .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), - .reserved = {0,0,0,0} }; static struct v4l2_fmtdesc pvr_fmtdesc [] = { diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index f495eeb..2834e3e 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1146,14 +1146,6 @@ leave: return ret; } -static int pwc_log_status(struct file *file, void *priv) -{ - struct pwc_device *pdev = video_drvdata(file); - - v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME); - return 0; -} - const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_querycap = pwc_querycap, .vidioc_enum_input = pwc_enum_input, @@ -1169,7 +1161,7 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_dqbuf = pwc_dqbuf, .vidioc_streamon = pwc_streamon, .vidioc_streamoff = pwc_streamoff, - .vidioc_log_status = pwc_log_status, + .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_enum_framesizes = pwc_enum_framesizes, .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_g_parm = pwc_g_parm, diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 0bd7da2..5a413f4 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -921,12 +921,12 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) /* "Safe default" - 13MHz */ recalculate_fifo_timeout(pcdev, 13000000); - clk_enable(pcdev->clk); + clk_prepare_enable(pcdev->clk); } static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) { - clk_disable(pcdev->clk); + clk_disable_unprepare(pcdev->clk); } static irqreturn_t pxa_camera_irq(int irq, void *data) diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 9937386..f6419b2 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -1407,18 +1407,7 @@ static struct i2c_driver rj54n1_i2c_driver = { .id_table = rj54n1_id, }; -static int __init rj54n1_mod_init(void) -{ - return i2c_add_driver(&rj54n1_i2c_driver); -} - -static void __exit rj54n1_mod_exit(void) -{ - i2c_del_driver(&rj54n1_i2c_driver); -} - -module_init(rj54n1_mod_init); -module_exit(rj54n1_mod_exit); +module_i2c_driver(rj54n1_i2c_driver); MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index c1bef61..4894cbb 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -134,7 +134,7 @@ /* usb config commands */ #define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) -#define CMD_2255 cpu_to_le32(0xc2255000) +#define CMD_2255 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)) @@ -852,15 +852,13 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - int index = 0; - if (f) - index = f->index; + int index = f->index; if (index >= ARRAY_SIZE(formats)) return -EINVAL; - if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) - return -EINVAL; + if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + return -EINVAL; dprintk(4, "name %s\n", formats[index].name); strlcpy(f->description, formats[index].name, sizeof(f->description)); f->pixelformat = formats[index].fourcc; @@ -2027,7 +2025,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) pdata[1]); offset = jj + PREFIX_SIZE; bframe = 1; - cc = pdword[1]; + cc = le32_to_cpu(pdword[1]); if (cc >= MAX_CHANNELS) { printk(KERN_ERR "bad channel\n"); @@ -2036,22 +2034,22 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) /* reverse it */ dev->cc = G_chnmap[cc]; channel = &dev->channel[dev->cc]; - payload = pdword[3]; + payload = le32_to_cpu(pdword[3]); if (payload > channel->req_image_size) { channel->bad_payload++; /* discard the bad frame */ return -EINVAL; } channel->pkt_size = payload; - channel->jpg_size = pdword[4]; + channel->jpg_size = le32_to_cpu(pdword[4]); break; case S2255_MARKER_RESPONSE: pdata += DEF_USB_BLOCK; jj += DEF_USB_BLOCK; - if (pdword[1] >= MAX_CHANNELS) + if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) break; - cc = G_chnmap[pdword[1]]; + cc = G_chnmap[le32_to_cpu(pdword[1])]; if (cc >= MAX_CHANNELS) break; channel = &dev->channel[cc]; @@ -2074,11 +2072,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) wake_up(&dev->fw_data->wait_fw); break; case S2255_RESPONSE_STATUS: - channel->vidstatus = pdword[3]; + channel->vidstatus = le32_to_cpu(pdword[3]); channel->vidstatus_ready = 1; wake_up(&channel->wait_vidstatus); dprintk(5, "got vidstatus %x chan %d\n", - pdword[3], cc); + le32_to_cpu(pdword[3]), cc); break; default: printk(KERN_INFO "s2255 unknown resp\n"); @@ -2605,10 +2603,11 @@ static int s2255_probe(struct usb_interface *interface, __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) + dev->dsp_fw_ver = le32_to_cpu(*pRel); + if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); - if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER) + if (dev->pid == 0x2257 && + dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) printk(KERN_WARNING "s2255: 2257 requires firmware %d" " or above.\n", S2255_MIN_DSP_COLORFILTER); } diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c index 0df7f2a..6625e46 100644 --- a/drivers/media/video/s5k6aa.c +++ b/drivers/media/video/s5k6aa.c @@ -1582,8 +1582,8 @@ static int s5k6aa_probe(struct i2c_client *client, s5k6aa->inv_vflip = pdata->vert_flip; sd = &s5k6aa->sd; - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); sd->internal_ops = &s5k6aa_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1663,18 +1663,7 @@ static struct i2c_driver s5k6aa_i2c_driver = { .id_table = s5k6aa_id, }; -static int __init s5k6aa_init(void) -{ - return i2c_add_driver(&s5k6aa_i2c_driver); -} - -static void __exit s5k6aa_exit(void) -{ - i2c_del_driver(&s5k6aa_i2c_driver); -} - -module_init(s5k6aa_init); -module_exit(s5k6aa_exit); +module_i2c_driver(s5k6aa_i2c_driver); MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index a9e9653..b06efd2 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1019,52 +1019,117 @@ static int fimc_cap_dqbuf(struct file *file, void *priv, return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); } -static int fimc_cap_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cr) +static int fimc_cap_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; - if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return -EINVAL; + return vb2_create_bufs(&fimc->vid_cap.vbq, create); +} - cr->bounds.left = 0; - cr->bounds.top = 0; - cr->bounds.width = f->o_width; - cr->bounds.height = f->o_height; - cr->defrect = cr->bounds; +static int fimc_cap_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct fimc_dev *fimc = video_drvdata(file); - return 0; + return vb2_prepare_buf(&fimc->vid_cap.vbq, b); } -static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) +static int fimc_cap_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_frame *f = &ctx->s_frame; - cr->c.left = f->offs_h; - cr->c.top = f->offs_v; - cr->c.width = f->width; - cr->c.height = f->height; + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; - return 0; + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + f = &ctx->d_frame; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = f->o_width; + s->r.height = f->o_height; + return 0; + + case V4L2_SEL_TGT_COMPOSE_ACTIVE: + f = &ctx->d_frame; + case V4L2_SEL_TGT_CROP_ACTIVE: + s->r.left = f->offs_h; + s->r.top = f->offs_v; + s->r.width = f->width; + s->r.height = f->height; + return 0; + } + + return -EINVAL; } -static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int fimc_cap_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) { struct fimc_dev *fimc = video_drvdata(file); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct fimc_frame *ff; + struct v4l2_rect rect = s->r; + struct fimc_frame *f; unsigned long flags; + unsigned int pad; - fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK); - ff = &ctx->s_frame; + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_ACTIVE: + f = &ctx->d_frame; + pad = FIMC_SD_PAD_SOURCE; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_ACTIVE: + f = &ctx->s_frame; + pad = FIMC_SD_PAD_SINK; + break; + default: + return -EINVAL; + } + + fimc_capture_try_crop(ctx, &rect, pad); + + if (s->flags & V4L2_SEL_FLAG_LE && + !enclosed_rectangle(&rect, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && + !enclosed_rectangle(&s->r, &rect)) + return -ERANGE; + + s->r = rect; spin_lock_irqsave(&fimc->slock, flags); - set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height); - set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + set_frame_crop(f, s->r.left, s->r.top, s->r.width, + s->r.height); spin_unlock_irqrestore(&fimc->slock, flags); + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); return 0; } @@ -1082,12 +1147,14 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_qbuf = fimc_cap_qbuf, .vidioc_dqbuf = fimc_cap_dqbuf, + .vidioc_prepare_buf = fimc_cap_prepare_buf, + .vidioc_create_bufs = fimc_cap_create_bufs, + .vidioc_streamon = fimc_cap_streamon, .vidioc_streamoff = fimc_cap_streamoff, - .vidioc_g_crop = fimc_cap_g_crop, - .vidioc_s_crop = fimc_cap_s_crop, - .vidioc_cropcap = fimc_cap_cropcap, + .vidioc_g_selection = fimc_cap_g_selection, + .vidioc_s_selection = fimc_cap_s_selection, .vidioc_enum_input = fimc_cap_enum_input, .vidioc_s_input = fimc_cap_s_input, diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 81bcbb9..e184e65 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1602,24 +1602,35 @@ static void fimc_clk_put(struct fimc_dev *fimc) { int i; for (i = 0; i < fimc->num_clocks; i++) { - if (fimc->clock[i]) - clk_put(fimc->clock[i]); + if (IS_ERR_OR_NULL(fimc->clock[i])) + continue; + clk_unprepare(fimc->clock[i]); + clk_put(fimc->clock[i]); + fimc->clock[i] = NULL; } } static int fimc_clk_get(struct fimc_dev *fimc) { - int i; + int i, ret; + for (i = 0; i < fimc->num_clocks; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); - if (!IS_ERR_OR_NULL(fimc->clock[i])) - continue; - dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n", - fimc_clocks[i]); - return -ENXIO; + if (IS_ERR(fimc->clock[i])) + goto err; + ret = clk_prepare(fimc->clock[i]); + if (ret < 0) { + clk_put(fimc->clock[i]); + fimc->clock[i] = NULL; + goto err; + } } - return 0; +err: + fimc_clk_put(fimc); + dev_err(&fimc->pdev->dev, "failed to get clock: %s\n", + fimc_clocks[i]); + return -ENXIO; } static int fimc_m2m_suspend(struct fimc_dev *fimc) @@ -1667,8 +1678,6 @@ static int fimc_probe(struct platform_device *pdev) struct s5p_platform_fimc *pdata; int ret = 0; - dev_dbg(&pdev->dev, "%s():\n", __func__); - drv_data = (struct samsung_fimc_driverdata *) platform_get_device_id(pdev)->driver_data; @@ -1678,7 +1687,7 @@ static int fimc_probe(struct platform_device *pdev) return -EINVAL; } - fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL); + fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; @@ -1689,51 +1698,35 @@ static int fimc_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; fimc->pdata = pdata; - init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to find the registers\n"); - ret = -ENOENT; - goto err_info; - } - - fimc->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - if (!fimc->regs_res) { - dev_err(&pdev->dev, "failed to obtain register region\n"); - ret = -ENOENT; - goto err_info; - } - - fimc->regs = ioremap(res->start, resource_size(res)); - if (!fimc->regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - ret = -ENXIO; - goto err_req_region; + fimc->regs = devm_request_and_ioremap(&pdev->dev, res); + if (fimc->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get IRQ resource\n"); - ret = -ENXIO; - goto err_regs_unmap; + if (res == NULL) { + dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + return -ENXIO; } fimc->irq = res->start; fimc->num_clocks = MAX_FIMC_CLOCKS; ret = fimc_clk_get(fimc); if (ret) - goto err_regs_unmap; + return ret; clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); clk_enable(fimc->clock[CLK_BUS]); platform_set_drvdata(pdev, fimc); - ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc); + ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler, + 0, pdev->name, fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); goto err_clk; @@ -1742,7 +1735,7 @@ static int fimc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - goto err_irq; + goto err_clk; /* Initialize contiguous memory allocator */ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(fimc->alloc_ctx)) { @@ -1757,17 +1750,8 @@ static int fimc_probe(struct platform_device *pdev) err_pm: pm_runtime_put(&pdev->dev); -err_irq: - free_irq(fimc->irq, fimc); err_clk: fimc_clk_put(fimc); -err_regs_unmap: - iounmap(fimc->regs); -err_req_region: - release_resource(fimc->regs_res); - kfree(fimc->regs_res); -err_info: - kfree(fimc); return ret; } @@ -1854,11 +1838,6 @@ static int __devexit fimc_remove(struct platform_device *pdev) clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); - free_irq(fimc->irq, fimc); - iounmap(fimc->regs); - release_resource(fimc->regs_res); - kfree(fimc->regs_res); - kfree(fimc); dev_info(&pdev->dev, "driver unloaded\n"); return 0; diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 4e20560..a18291e 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -434,7 +434,6 @@ struct fimc_ctx; * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers - * @regs_res: the resource claimed for IO registers * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @v4l2_dev: root v4l2_device @@ -454,7 +453,6 @@ struct fimc_dev { u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; - struct resource *regs_res; int irq; wait_queue_head_t irq_queue; struct v4l2_device *v4l2_dev; diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 8ea4ee1..62ed37e 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -344,16 +344,13 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd) return -ENODEV; ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); - put_driver(driver); if (ret) return ret; driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) { + if (driver) ret = driver_for_each_device(driver, NULL, fmd, csis_register_callback); - put_driver(driver); - } return ret; } @@ -753,7 +750,7 @@ static int __devinit fimc_md_probe(struct platform_device *pdev) struct fimc_md *fmd; int ret; - fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL); + fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL); if (!fmd) return -ENOMEM; @@ -774,7 +771,7 @@ static int __devinit fimc_md_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); - goto err1; + return ret; } ret = media_device_register(&fmd->media_dev); if (ret < 0) { @@ -816,8 +813,6 @@ err3: fimc_md_unregister_entities(fmd); err2: v4l2_device_unregister(&fmd->v4l2_dev); -err1: - kfree(fmd); return ret; } @@ -831,7 +826,6 @@ static int __devexit fimc_md_remove(struct platform_device *pdev) fimc_md_unregister_entities(fmd); media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); - kfree(fmd); return 0; } diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index 130335c..f44f690 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> + * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki, <s.nawrocki@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -100,7 +100,6 @@ enum { * @pads: CSIS pads array * @sd: v4l2_subdev associated with CSIS device instance * @pdev: CSIS platform device - * @regs_res: requested I/O register memory resource * @regs: mmaped I/O registers memory * @clock: CSIS clocks * @irq: requested s5p-mipi-csis irq number @@ -113,7 +112,6 @@ struct csis_state { struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_subdev sd; struct platform_device *pdev; - struct resource *regs_res; void __iomem *regs; struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; struct clk *clock[NUM_CSIS_CLOCKS]; @@ -258,26 +256,36 @@ static void s5pcsis_clk_put(struct csis_state *state) { int i; - for (i = 0; i < NUM_CSIS_CLOCKS; i++) - if (!IS_ERR_OR_NULL(state->clock[i])) - clk_put(state->clock[i]); + for (i = 0; i < NUM_CSIS_CLOCKS; i++) { + if (IS_ERR_OR_NULL(state->clock[i])) + continue; + clk_unprepare(state->clock[i]); + clk_put(state->clock[i]); + state->clock[i] = NULL; + } } static int s5pcsis_clk_get(struct csis_state *state) { struct device *dev = &state->pdev->dev; - int i; + int i, ret; for (i = 0; i < NUM_CSIS_CLOCKS; i++) { state->clock[i] = clk_get(dev, csi_clock_name[i]); - if (IS_ERR(state->clock[i])) { - s5pcsis_clk_put(state); - dev_err(dev, "failed to get clock: %s\n", - csi_clock_name[i]); - return -ENXIO; + if (IS_ERR(state->clock[i])) + goto err; + ret = clk_prepare(state->clock[i]); + if (ret < 0) { + clk_put(state->clock[i]); + state->clock[i] = NULL; + goto err; } } return 0; +err: + s5pcsis_clk_put(state); + dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]); + return -ENXIO; } static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) @@ -480,12 +488,11 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) { struct s5p_platform_mipi_csis *pdata; struct resource *mem_res; - struct resource *regs_res; struct csis_state *state; int ret = -ENOMEM; int i; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; @@ -495,52 +502,27 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (pdata == NULL || pdata->phy_enable == NULL) { dev_err(&pdev->dev, "Platform data not fully specified\n"); - goto e_free; + return -EINVAL; } if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) || pdata->lanes > CSIS0_MAX_LANES) { - ret = -EINVAL; dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", pdata->lanes); - goto e_free; + return -EINVAL; } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "Failed to get IO memory region\n"); - goto e_free; + state->regs = devm_request_and_ioremap(&pdev->dev, mem_res); + if (state->regs == NULL) { + dev_err(&pdev->dev, "Failed to request and remap io memory\n"); + return -ENXIO; } - regs_res = request_mem_region(mem_res->start, resource_size(mem_res), - pdev->name); - if (!regs_res) { - dev_err(&pdev->dev, "Failed to request IO memory region\n"); - goto e_free; - } - state->regs_res = regs_res; - - state->regs = ioremap(mem_res->start, resource_size(mem_res)); - if (!state->regs) { - dev_err(&pdev->dev, "Failed to remap IO region\n"); - goto e_reqmem; - } - - ret = s5pcsis_clk_get(state); - if (ret) - goto e_unmap; - - clk_enable(state->clock[CSIS_CLK_MUX]); - if (pdata->clk_rate) - clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); - else - dev_WARN(&pdev->dev, "No clock frequency specified!\n"); - state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { - ret = state->irq; dev_err(&pdev->dev, "Failed to get irq\n"); - goto e_clkput; + return state->irq; } for (i = 0; i < CSIS_NUM_SUPPLIES; i++) @@ -549,12 +531,22 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) + return ret; + + ret = s5pcsis_clk_get(state); + if (ret) goto e_clkput; - ret = request_irq(state->irq, s5pcsis_irq_handler, 0, - dev_name(&pdev->dev), state); + clk_enable(state->clock[CSIS_CLK_MUX]); + if (pdata->clk_rate) + clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); + else + dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + + ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, + 0, dev_name(&pdev->dev), state); if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); + dev_err(&pdev->dev, "Interrupt request failed\n"); goto e_regput; } @@ -573,7 +565,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) ret = media_entity_init(&state->sd.entity, CSIS_PADS_NUM, state->pads, 0); if (ret < 0) - goto e_irqfree; + goto e_clkput; /* This allows to retrieve the platform device id by the host driver */ v4l2_set_subdevdata(&state->sd, pdev); @@ -582,22 +574,13 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &state->sd); pm_runtime_enable(&pdev->dev); - return 0; -e_irqfree: - free_irq(state->irq, state); e_regput: regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); e_clkput: clk_disable(state->clock[CSIS_CLK_MUX]); s5pcsis_clk_put(state); -e_unmap: - iounmap(state->regs); -e_reqmem: - release_mem_region(regs_res->start, resource_size(regs_res)); -e_free: - kfree(state); return ret; } @@ -699,21 +682,15 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev) { struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct csis_state *state = sd_to_csis_state(sd); - struct resource *res = state->regs_res; pm_runtime_disable(&pdev->dev); - s5pcsis_suspend(&pdev->dev); + s5pcsis_pm_suspend(&pdev->dev, false); clk_disable(state->clock[CSIS_CLK_MUX]); pm_runtime_set_suspended(&pdev->dev); - s5pcsis_clk_put(state); regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); media_entity_cleanup(&state->sd.entity); - free_irq(state->irq, state); - iounmap(state->regs); - release_mem_region(res->start, resource_size(res)); - kfree(state); return 0; } diff --git a/drivers/media/video/s5p-g2d/g2d-hw.c b/drivers/media/video/s5p-g2d/g2d-hw.c index 39937cf..5b86cbe 100644 --- a/drivers/media/video/s5p-g2d/g2d-hw.c +++ b/drivers/media/video/s5p-g2d/g2d-hw.c @@ -77,6 +77,11 @@ void g2d_set_rop4(struct g2d_dev *d, u32 r) w(r, ROP4_REG); } +void g2d_set_flip(struct g2d_dev *d, u32 r) +{ + w(r, SRC_MSK_DIRECT_REG); +} + u32 g2d_cmd_stretch(u32 e) { e &= 1; diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index febaa67..789de74 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -178,6 +178,9 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl) { struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx, ctrl_handler); + unsigned long flags; + + spin_lock_irqsave(&ctx->dev->ctrl_lock, flags); switch (ctrl->id) { case V4L2_CID_COLORFX: if (ctrl->val == V4L2_COLORFX_NEGATIVE) @@ -185,10 +188,13 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl) else ctx->rop = ROP4_COPY; break; - default: - v4l2_err(&ctx->dev->v4l2_dev, "unknown control\n"); - return -EINVAL; + + case V4L2_CID_HFLIP: + ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1); + break; + } + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags); return 0; } @@ -200,11 +206,13 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx) { struct g2d_dev *dev = ctx->dev; - v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); - if (ctx->ctrl_handler.error) { - v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n"); - return ctx->ctrl_handler.error; - } + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std_menu( &ctx->ctrl_handler, @@ -215,10 +223,14 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx) V4L2_COLORFX_NONE); if (ctx->ctrl_handler.error) { - v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n"); - return ctx->ctrl_handler.error; + int err = ctx->ctrl_handler.error; + v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n"); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; } + v4l2_ctrl_cluster(2, &ctx->ctrl_hflip); + return 0; } @@ -547,6 +559,7 @@ static void device_run(void *prv) struct g2d_ctx *ctx = prv; struct g2d_dev *dev = ctx->dev; struct vb2_buffer *src, *dst; + unsigned long flags; u32 cmd = 0; dev->curr = ctx; @@ -557,6 +570,8 @@ static void device_run(void *prv) clk_enable(dev->gate); g2d_reset(dev); + spin_lock_irqsave(&dev->ctrl_lock, flags); + g2d_set_src_size(dev, &ctx->in); g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0)); @@ -564,11 +579,15 @@ static void device_run(void *prv) g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0)); g2d_set_rop4(dev, ctx->rop); + g2d_set_flip(dev, ctx->flip); + if (ctx->in.c_width != ctx->out.c_width || ctx->in.c_height != ctx->out.c_height) cmd |= g2d_cmd_stretch(1); g2d_set_cmd(dev, cmd); g2d_start(dev); + + spin_unlock_irqrestore(&dev->ctrl_lock, flags); } static irqreturn_t g2d_isr(int irq, void *prv) @@ -658,7 +677,7 @@ static int g2d_probe(struct platform_device *pdev) dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - spin_lock_init(&dev->irqlock); + spin_lock_init(&dev->ctrl_lock); mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); init_waitqueue_head(&dev->irq_queue); @@ -693,18 +712,30 @@ static int g2d_probe(struct platform_device *pdev) goto unmap_regs; } + ret = clk_prepare(dev->clk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare g2d clock\n"); + goto put_clk; + } + dev->gate = clk_get(&pdev->dev, "fimg2d"); if (IS_ERR_OR_NULL(dev->gate)) { dev_err(&pdev->dev, "failed to get g2d clock gate\n"); ret = -ENXIO; - goto put_clk; + goto unprep_clk; + } + + ret = clk_prepare(dev->gate); + if (ret) { + dev_err(&pdev->dev, "failed to prepare g2d clock gate\n"); + goto put_clk_gate; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "failed to find IRQ\n"); ret = -ENXIO; - goto put_clk_gate; + goto unprep_clk_gate; } dev->irq = res->start; @@ -764,8 +795,12 @@ alloc_ctx_cleanup: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); rel_irq: free_irq(dev->irq, dev); +unprep_clk_gate: + clk_unprepare(dev->gate); put_clk_gate: clk_put(dev->gate); +unprep_clk: + clk_unprepare(dev->clk); put_clk: clk_put(dev->clk); unmap_regs: @@ -787,7 +822,9 @@ static int g2d_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); free_irq(dev->irq, dev); + clk_unprepare(dev->gate); clk_put(dev->gate); + clk_unprepare(dev->clk); clk_put(dev->clk); iounmap(dev->regs); release_resource(dev->res_regs); diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h index 5eae901..1b82065 100644 --- a/drivers/media/video/s5p-g2d/g2d.h +++ b/drivers/media/video/s5p-g2d/g2d.h @@ -20,7 +20,7 @@ struct g2d_dev { struct v4l2_m2m_dev *m2m_dev; struct video_device *vfd; struct mutex mutex; - spinlock_t irqlock; + spinlock_t ctrl_lock; atomic_t num_inst; struct vb2_alloc_ctx *alloc_ctx; struct resource *res_regs; @@ -57,8 +57,11 @@ struct g2d_ctx { struct v4l2_m2m_ctx *m2m_ctx; struct g2d_frame in; struct g2d_frame out; + struct v4l2_ctrl *ctrl_hflip; + struct v4l2_ctrl *ctrl_vflip; struct v4l2_ctrl_handler ctrl_handler; u32 rop; + u32 flip; }; struct g2d_fmt { @@ -77,6 +80,7 @@ void g2d_set_dst_addr(struct g2d_dev *d, dma_addr_t a); void g2d_start(struct g2d_dev *d); void g2d_clear_int(struct g2d_dev *d); void g2d_set_rop4(struct g2d_dev *d, u32 r); +void g2d_set_flip(struct g2d_dev *d, u32 r); u32 g2d_cmd_stretch(u32 e); void g2d_set_cmd(struct g2d_dev *d, u32 c); diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 1105a87..5a49c30 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -32,10 +32,9 @@ static struct s5p_jpeg_fmt formats_enc[] = { { - .name = "YUV 4:2:0 planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .colplanes = 3, + .name = "JPEG JFIF", + .fourcc = V4L2_PIX_FMT_JPEG, + .colplanes = 1, .types = MEM2MEM_CAPTURE, }, { @@ -43,7 +42,7 @@ static struct s5p_jpeg_fmt formats_enc[] = { .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .colplanes = 1, - .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, + .types = MEM2MEM_OUTPUT, }, { .name = "RGB565", @@ -203,6 +202,16 @@ static const unsigned char hactblg0[162] = { 0xf9, 0xfa }; +static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) +{ + return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); +} + +static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct s5p_jpeg_ctx, fh); +} + static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl, unsigned long tab, int len) { @@ -269,6 +278,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, __u32 pixelformat); +static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx); static int s5p_jpeg_open(struct file *file) { @@ -276,12 +286,18 @@ static int s5p_jpeg_open(struct file *file) struct video_device *vfd = video_devdata(file); struct s5p_jpeg_ctx *ctx; struct s5p_jpeg_fmt *out_fmt; + int ret = 0; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) return -ENOMEM; - file->private_data = ctx; + v4l2_fh_init(&ctx->fh, vfd); + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + ctx->jpeg = jpeg; if (vfd == jpeg->vfd_encoder) { ctx->mode = S5P_JPEG_ENCODE; @@ -291,24 +307,35 @@ static int s5p_jpeg_open(struct file *file) out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG); } + ret = s5p_jpeg_controls_create(ctx); + if (ret < 0) + goto error; + ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { - int err = PTR_ERR(ctx->m2m_ctx); - kfree(ctx); - return err; + ret = PTR_ERR(ctx->m2m_ctx); + goto error; } ctx->out_q.fmt = out_fmt; ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV); - return 0; + +error: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; } static int s5p_jpeg_release(struct file *file) { - struct s5p_jpeg_ctx *ctx = file->private_data; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); kfree(ctx); return 0; @@ -317,14 +344,14 @@ static int s5p_jpeg_release(struct file *file) static unsigned int s5p_jpeg_poll(struct file *file, struct poll_table_struct *wait) { - struct s5p_jpeg_ctx *ctx = file->private_data; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma) { - struct s5p_jpeg_ctx *ctx = file->private_data; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } @@ -448,7 +475,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, static int s5p_jpeg_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) { strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder", @@ -497,9 +524,7 @@ static int enum_fmt(struct s5p_jpeg_fmt *formats, int n, static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct s5p_jpeg_ctx *ctx; - - ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, @@ -511,9 +536,7 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct s5p_jpeg_ctx *ctx; - - ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, @@ -538,7 +561,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct vb2_queue *vq; struct s5p_jpeg_q_data *q_data = NULL; struct v4l2_pix_format *pix = &f->fmt.pix; - struct s5p_jpeg_ctx *ct = priv; + struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); if (!vq) @@ -659,8 +682,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); struct s5p_jpeg_fmt *fmt; - struct s5p_jpeg_ctx *ctx = priv; fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { @@ -676,8 +699,8 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); struct s5p_jpeg_fmt *fmt; - struct s5p_jpeg_ctx *ctx = priv; fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { @@ -728,7 +751,7 @@ static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv, if (ret) return ret; - return s5p_jpeg_s_fmt(priv, f); + return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, @@ -740,13 +763,13 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, if (ret) return ret; - return s5p_jpeg_s_fmt(priv, f); + return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } static int s5p_jpeg_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -754,14 +777,14 @@ static int s5p_jpeg_reqbufs(struct file *file, void *priv, static int s5p_jpeg_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); } static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } @@ -769,7 +792,7 @@ static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) static int s5p_jpeg_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } @@ -777,7 +800,7 @@ static int s5p_jpeg_dqbuf(struct file *file, void *priv, static int s5p_jpeg_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -785,7 +808,7 @@ static int s5p_jpeg_streamon(struct file *file, void *priv, static int s5p_jpeg_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } @@ -793,7 +816,7 @@ static int s5p_jpeg_streamoff(struct file *file, void *priv, int s5p_jpeg_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -822,33 +845,89 @@ int s5p_jpeg_g_selection(struct file *file, void *priv, return 0; } -static int s5p_jpeg_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *compr) +/* + * V4L2 controls + */ + +static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned long flags; - if (ctx->mode == S5P_JPEG_DECODE) - return -ENOTTY; + switch (ctrl->id) { + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + spin_lock_irqsave(&jpeg->slock, flags); - memset(compr, 0, sizeof(*compr)); - compr->quality = ctx->compr_quality; + WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY); + if (ctx->subsampling > 2) + ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + else + ctrl->val = ctx->subsampling; + spin_unlock_irqrestore(&jpeg->slock, flags); + break; + } return 0; } -static int s5p_jpeg_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *compr) +static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) { - struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; - if (ctx->mode == S5P_JPEG_DECODE) - return -ENOTTY; + spin_lock_irqsave(&ctx->jpeg->slock, flags); - compr->quality = clamp(compr->quality, S5P_JPEG_COMPR_QUAL_BEST, - S5P_JPEG_COMPR_QUAL_WORST); + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val; + break; + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->restart_interval = ctrl->val; + break; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + ctx->subsampling = ctrl->val; + break; + } - ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - compr->quality; + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); + return 0; +} + +static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = { + .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl, + .s_ctrl = s5p_jpeg_s_ctrl, +}; +static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) +{ + unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */ + struct v4l2_ctrl *ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + if (ctx->mode == S5P_JPEG_ENCODE) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, 3, 1, 3); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_RESTART_INTERVAL, + 0, 3, 0xffff, 0); + mask = ~0x06; /* 422, 420 */ + } + + ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask, + V4L2_JPEG_CHROMA_SUBSAMPLING_422); + + if (ctx->ctrl_handler.error) + return ctx->ctrl_handler.error; + + if (ctx->mode == S5P_JPEG_DECODE) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; return 0; } @@ -877,9 +956,6 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { .vidioc_streamoff = s5p_jpeg_streamoff, .vidioc_g_selection = s5p_jpeg_g_selection, - - .vidioc_g_jpegcomp = s5p_jpeg_g_jpegcomp, - .vidioc_s_jpegcomp = s5p_jpeg_s_jpegcomp, }; /* @@ -908,13 +984,8 @@ static void s5p_jpeg_device_run(void *priv) jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565); else jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422); - if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) - jpeg_subsampling_mode(jpeg->regs, - S5P_JPEG_SUBSAMPLING_422); - else - jpeg_subsampling_mode(jpeg->regs, - S5P_JPEG_SUBSAMPLING_420); - jpeg_dri(jpeg->regs, 0); + jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); + jpeg_dri(jpeg->regs, ctx->restart_interval); jpeg_x(jpeg->regs, ctx->out_q.w); jpeg_y(jpeg->regs, ctx->out_q.h); jpeg_imgadr(jpeg->regs, src_addr); @@ -953,14 +1024,18 @@ static void s5p_jpeg_device_run(void *priv) jpeg_htbl_dc(jpeg->regs, 2); jpeg_htbl_ac(jpeg->regs, 3); jpeg_htbl_dc(jpeg->regs, 3); - } else { + } else { /* S5P_JPEG_DECODE */ jpeg_rst_int_enable(jpeg->regs, true); jpeg_data_num_int_enable(jpeg->regs, true); jpeg_final_mcu_num_int_enable(jpeg->regs, true); - jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) + jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + else + jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); jpeg_jpgadr(jpeg->regs, src_addr); jpeg_imgadr(jpeg->regs, dst_addr); } + jpeg_start(jpeg->regs); } @@ -1162,6 +1237,8 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) bool timer_elapsed = false; bool op_completed = false; + spin_lock(&jpeg->slock); + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); @@ -1192,6 +1269,9 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) v4l2_m2m_buf_done(dst_buf, state); v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx); + curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs); + spin_unlock(&jpeg->slock); + jpeg_clear_int(jpeg->regs); return IRQ_HANDLED; @@ -1215,6 +1295,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return -ENOMEM; mutex_init(&jpeg->lock); + spin_lock_init(&jpeg->slock); jpeg->dev = &pdev->dev; /* memory-mapped registers */ diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h index facad61..38d7367 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.h +++ b/drivers/media/video/s5p-jpeg/jpeg-core.h @@ -14,6 +14,8 @@ #define JPEG_CORE_H_ #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> #define S5P_JPEG_M2M_NAME "s5p-jpeg" @@ -47,6 +49,7 @@ /** * struct s5p_jpeg - JPEG IP abstraction * @lock: the mutex protecting this structure + * @slock: spinlock protecting the device contexts * @v4l2_dev: v4l2 device for mem2mem mode * @vfd_encoder: video device node for encoder mem2mem mode * @vfd_decoder: video device node for decoder mem2mem mode @@ -60,6 +63,7 @@ */ struct s5p_jpeg { struct mutex lock; + struct spinlock slock; struct v4l2_device v4l2_dev; struct video_device *vfd_encoder; @@ -117,15 +121,20 @@ struct s5p_jpeg_q_data { * @out_q: source (output) queue information * @cap_fmt: destination (capture) queue queue information * @hdr_parsed: set if header has been parsed during decompression + * @ctrl_handler: controls handler */ struct s5p_jpeg_ctx { struct s5p_jpeg *jpeg; unsigned int mode; - unsigned int compr_quality; + unsigned short compr_quality; + unsigned short restart_interval; + unsigned short subsampling; struct v4l2_m2m_ctx *m2m_ctx; struct s5p_jpeg_q_data out_q; struct s5p_jpeg_q_data cap_q; + struct v4l2_fh fh; bool hdr_parsed; + struct v4l2_ctrl_handler ctrl_handler; }; /** diff --git a/drivers/media/video/s5p-jpeg/jpeg-hw.h b/drivers/media/video/s5p-jpeg/jpeg-hw.h index e10c744..f12f0fd 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-hw.h +++ b/drivers/media/video/s5p-jpeg/jpeg-hw.h @@ -13,6 +13,7 @@ #define JPEG_HW_H_ #include <linux/io.h> +#include <linux/videodev2.h> #include "jpeg-hw.h" #include "jpeg-regs.h" @@ -25,8 +26,6 @@ #define S5P_JPEG_DECODE 1 #define S5P_JPEG_RAW_IN_565 0 #define S5P_JPEG_RAW_IN_422 1 -#define S5P_JPEG_SUBSAMPLING_422 0 -#define S5P_JPEG_SUBSAMPLING_420 1 #define S5P_JPEG_RAW_OUT_422 0 #define S5P_JPEG_RAW_OUT_420 1 @@ -91,21 +90,26 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode) writel(reg, regs + S5P_JPGMOD); } -static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned long mode) +static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) { unsigned long reg, m; - m = S5P_SUBSAMPLING_MODE_422; - if (mode == S5P_JPEG_SUBSAMPLING_422) - m = S5P_SUBSAMPLING_MODE_422; - else if (mode == S5P_JPEG_SUBSAMPLING_420) + if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420) m = S5P_SUBSAMPLING_MODE_420; + else + m = S5P_SUBSAMPLING_MODE_422; + reg = readl(regs + S5P_JPGMOD); reg &= ~S5P_SUBSAMPLING_MODE_MASK; reg |= m; writel(reg, regs + S5P_JPGMOD); } +static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs) +{ + return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK; +} + static inline void jpeg_dri(void __iomem *regs, unsigned int dri) { unsigned long reg; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c index f6a3035..738a607 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c @@ -41,15 +41,29 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME); if (IS_ERR(pm->clock_gate)) { mfc_err("Failed to get clock-gating control\n"); - ret = -ENOENT; + ret = PTR_ERR(pm->clock_gate); goto err_g_ip_clk; } + + ret = clk_prepare(pm->clock_gate); + if (ret) { + mfc_err("Failed to preapre clock-gating control\n"); + goto err_p_ip_clk; + } + pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME); if (IS_ERR(pm->clock)) { mfc_err("Failed to get MFC clock\n"); - ret = -ENOENT; + ret = PTR_ERR(pm->clock); goto err_g_ip_clk_2; } + + ret = clk_prepare(pm->clock); + if (ret) { + mfc_err("Failed to prepare MFC clock\n"); + goto err_p_ip_clk_2; + } + atomic_set(&pm->power, 0); #ifdef CONFIG_PM_RUNTIME pm->device = &dev->plat_dev->dev; @@ -59,7 +73,11 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) atomic_set(&clk_ref, 0); #endif return 0; +err_p_ip_clk_2: + clk_put(pm->clock); err_g_ip_clk_2: + clk_unprepare(pm->clock_gate); +err_p_ip_clk: clk_put(pm->clock_gate); err_g_ip_clk: return ret; @@ -67,7 +85,9 @@ err_g_ip_clk: void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) { + clk_unprepare(pm->clock_gate); clk_put(pm->clock_gate); + clk_unprepare(pm->clock); clk_put(pm->clock); #ifdef CONFIG_PM_RUNTIME pm_runtime_disable(pm->device); diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig index f2a0977..f248b28 100644 --- a/drivers/media/video/s5p-tv/Kconfig +++ b/drivers/media/video/s5p-tv/Kconfig @@ -46,6 +46,16 @@ config VIDEO_SAMSUNG_S5P_HDMIPHY as module. It is an I2C driver, that exposes a V4L2 subdev for use by other drivers. +config VIDEO_SAMSUNG_S5P_SII9234 + tristate "Samsung SII9234 Driver" + depends on VIDEO_DEV && VIDEO_V4L2 && I2C + depends on VIDEO_SAMSUNG_S5P_TV + help + Say Y here if you want support for the MHL interface + in S5P Samsung SoC. The driver can be compiled + as module. It is an I2C driver, that exposes a V4L2 + subdev for use by other drivers. + config VIDEO_SAMSUNG_S5P_SDO tristate "Samsung Analog TV Driver" depends on VIDEO_DEV && VIDEO_V4L2 diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile index 37e4c17..f49e756 100644 --- a/drivers/media/video/s5p-tv/Makefile +++ b/drivers/media/video/s5p-tv/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o s5p-hdmiphy-y += hdmiphy_drv.o +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o +s5p-sii9234-y += sii9234_drv.o obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o s5p-hdmi-y += hdmi_drv.o obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c index 8b41a04..4865d25 100644 --- a/drivers/media/video/s5p-tv/hdmi_drv.c +++ b/drivers/media/video/s5p-tv/hdmi_drv.c @@ -30,6 +30,7 @@ #include <linux/clk.h> #include <linux/regulator/consumer.h> +#include <media/s5p_hdmi.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> #include <media/v4l2-device.h> @@ -66,6 +67,8 @@ struct hdmi_device { struct v4l2_device v4l2_dev; /** subdev of HDMIPHY interface */ struct v4l2_subdev *phy_sd; + /** subdev of MHL interface */ + struct v4l2_subdev *mhl_sd; /** configuration of current graphic mode */ const struct hdmi_preset_conf *cur_conf; /** current preset */ @@ -74,10 +77,6 @@ struct hdmi_device { struct hdmi_resources res; }; -struct hdmi_driver_data { - int hdmiphy_bus; -}; - struct hdmi_tg_regs { u8 cmd; u8 h_fsz_l; @@ -129,23 +128,11 @@ struct hdmi_preset_conf { struct v4l2_mbus_framefmt mbus_fmt; }; -/* I2C module and id for HDMIPHY */ -static struct i2c_board_info hdmiphy_info = { - I2C_BOARD_INFO("hdmiphy", 0x38), -}; - -static struct hdmi_driver_data hdmi_driver_data[] = { - { .hdmiphy_bus = 3 }, - { .hdmiphy_bus = 8 }, -}; - static struct platform_device_id hdmi_driver_types[] = { { .name = "s5pv210-hdmi", - .driver_data = (unsigned long)&hdmi_driver_data[0], }, { .name = "exynos4-hdmi", - .driver_data = (unsigned long)&hdmi_driver_data[1], }, { /* end node */ } @@ -587,7 +574,15 @@ static int hdmi_streamon(struct hdmi_device *hdev) if (tries == 0) { dev_err(dev, "hdmiphy's pll could not reach steady state.\n"); v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - hdmi_dumpregs(hdev, "s_stream"); + hdmi_dumpregs(hdev, "hdmiphy - s_stream"); + return -EIO; + } + + /* starting MHL */ + ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1); + if (hdev->mhl_sd && ret) { + v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); + hdmi_dumpregs(hdev, "mhl - s_stream"); return -EIO; } @@ -618,6 +613,7 @@ static int hdmi_streamoff(struct hdmi_device *hdev) clk_set_parent(res->sclk_hdmi, res->sclk_pixel); clk_enable(res->sclk_hdmi); + v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0); v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); hdmi_dumpregs(hdev, "streamoff"); @@ -739,6 +735,7 @@ static int hdmi_runtime_suspend(struct device *dev) struct hdmi_device *hdev = sd_to_hdmi_dev(sd); dev_dbg(dev, "%s\n", __func__); + v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); hdmi_resource_poweroff(&hdev->res); return 0; } @@ -757,6 +754,11 @@ static int hdmi_runtime_resume(struct device *dev) if (ret) goto fail; + /* starting MHL */ + ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); + if (hdev->mhl_sd && ret) + goto fail; + dev_dbg(dev, "poweron succeed\n"); return 0; @@ -867,15 +869,21 @@ static int __devinit hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - struct i2c_adapter *phy_adapter; + struct i2c_adapter *adapter; struct v4l2_subdev *sd; struct hdmi_device *hdmi_dev = NULL; - struct hdmi_driver_data *drv_data; + struct s5p_hdmi_platform_data *pdata = dev->platform_data; int ret; dev_dbg(dev, "probe start\n"); - hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "platform data is missing\n"); + ret = -ENODEV; + goto fail; + } + + hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL); if (!hdmi_dev) { dev_err(dev, "out of memory\n"); ret = -ENOMEM; @@ -886,7 +894,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ret = hdmi_resources_init(hdmi_dev); if (ret) - goto fail_hdev; + goto fail; /* mapping HDMI registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -896,24 +904,26 @@ static int __devinit hdmi_probe(struct platform_device *pdev) goto fail_init; } - hdmi_dev->regs = ioremap(res->start, resource_size(res)); + hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (hdmi_dev->regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; - goto fail_hdev; + goto fail_init; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(dev, "get interrupt resource failed.\n"); ret = -ENXIO; - goto fail_regs; + goto fail_init; } - ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev); + ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0, + "hdmi", hdmi_dev); if (ret) { dev_err(dev, "request interrupt failed.\n"); - goto fail_regs; + goto fail_init; } hdmi_dev->irq = res->start; @@ -924,28 +934,54 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev); if (ret) { dev_err(dev, "could not register v4l2 device.\n"); - goto fail_irq; + goto fail_init; } - drv_data = (struct hdmi_driver_data *) - platform_get_device_id(pdev)->driver_data; - phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus); - if (phy_adapter == NULL) { - dev_err(dev, "adapter request failed\n"); + /* testing if hdmiphy info is present */ + if (!pdata->hdmiphy_info) { + dev_err(dev, "hdmiphy info is missing in platform data\n"); + ret = -ENXIO; + goto fail_vdev; + } + + adapter = i2c_get_adapter(pdata->hdmiphy_bus); + if (adapter == NULL) { + dev_err(dev, "hdmiphy adapter request failed\n"); ret = -ENXIO; goto fail_vdev; } hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, - phy_adapter, &hdmiphy_info, NULL); + adapter, pdata->hdmiphy_info, NULL); /* on failure or not adapter is no longer useful */ - i2c_put_adapter(phy_adapter); + i2c_put_adapter(adapter); if (hdmi_dev->phy_sd == NULL) { dev_err(dev, "missing subdev for hdmiphy\n"); ret = -ENODEV; goto fail_vdev; } + /* initialization of MHL interface if present */ + if (pdata->mhl_info) { + adapter = i2c_get_adapter(pdata->mhl_bus); + if (adapter == NULL) { + dev_err(dev, "MHL adapter request failed\n"); + ret = -ENXIO; + goto fail_vdev; + } + + hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board( + &hdmi_dev->v4l2_dev, adapter, + pdata->mhl_info, NULL); + /* on failure or not adapter is no longer useful */ + i2c_put_adapter(adapter); + if (hdmi_dev->mhl_sd == NULL) { + dev_err(dev, "missing subdev for MHL\n"); + ret = -ENODEV; + goto fail_vdev; + } + } + clk_enable(hdmi_dev->res.hdmi); pm_runtime_enable(dev); @@ -962,25 +998,16 @@ static int __devinit hdmi_probe(struct platform_device *pdev) /* storing subdev for call that have only access to struct device */ dev_set_drvdata(dev, sd); - dev_info(dev, "probe sucessful\n"); + dev_info(dev, "probe successful\n"); return 0; fail_vdev: v4l2_device_unregister(&hdmi_dev->v4l2_dev); -fail_irq: - free_irq(hdmi_dev->irq, hdmi_dev); - -fail_regs: - iounmap(hdmi_dev->regs); - fail_init: hdmi_resources_cleanup(hdmi_dev); -fail_hdev: - kfree(hdmi_dev); - fail: dev_err(dev, "probe failed\n"); return ret; @@ -996,11 +1023,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev) clk_disable(hdmi_dev->res.hdmi); v4l2_device_unregister(&hdmi_dev->v4l2_dev); disable_irq(hdmi_dev->irq); - free_irq(hdmi_dev->irq, hdmi_dev); - iounmap(hdmi_dev->regs); hdmi_resources_cleanup(hdmi_dev); - kfree(hdmi_dev); - dev_info(dev, "remove sucessful\n"); + dev_info(dev, "remove successful\n"); return 0; } diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c index 6693f4a..0afef77 100644 --- a/drivers/media/video/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c @@ -175,14 +175,4 @@ static struct i2c_driver hdmiphy_driver = { .id_table = hdmiphy_id, }; -static int __init hdmiphy_init(void) -{ - return i2c_add_driver(&hdmiphy_driver); -} -module_init(hdmiphy_init); - -static void __exit hdmiphy_exit(void) -{ - i2c_del_driver(&hdmiphy_driver); -} -module_exit(hdmiphy_exit); +module_i2c_driver(hdmiphy_driver); diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c index 0064309..a2c0c25 100644 --- a/drivers/media/video/s5p-tv/mixer_drv.c +++ b/drivers/media/video/s5p-tv/mixer_drv.c @@ -444,7 +444,7 @@ static int __devexit mxr_remove(struct platform_device *pdev) kfree(mdev); - dev_info(dev, "remove sucessful\n"); + dev_info(dev, "remove successful\n"); return 0; } diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 7884bae..f7ca5cc 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -58,7 +58,6 @@ static struct v4l2_subdev *find_and_register_subdev( } done: - put_driver(drv); return sd; } diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c index 059e774..f6bca2c 100644 --- a/drivers/media/video/s5p-tv/sdo_drv.c +++ b/drivers/media/video/s5p-tv/sdo_drv.c @@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) struct clk *sclk_vpll; dev_info(dev, "probe start\n"); - sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); if (!sdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; @@ -314,14 +314,14 @@ static int __devinit sdo_probe(struct platform_device *pdev) if (res == NULL) { dev_err(dev, "get memory resource failed.\n"); ret = -ENXIO; - goto fail_sdev; + goto fail; } - sdev->regs = ioremap(res->start, resource_size(res)); + sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (sdev->regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; - goto fail_sdev; + goto fail; } /* acquiring interrupt */ @@ -329,12 +329,13 @@ static int __devinit sdo_probe(struct platform_device *pdev) if (res == NULL) { dev_err(dev, "get interrupt resource failed.\n"); ret = -ENXIO; - goto fail_regs; + goto fail; } - ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev); + ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0, + "s5p-sdo", sdev); if (ret) { dev_err(dev, "request interrupt failed.\n"); - goto fail_regs; + goto fail; } sdev->irq = res->start; @@ -343,7 +344,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) if (IS_ERR_OR_NULL(sdev->sclk_dac)) { dev_err(dev, "failed to get clock 'sclk_dac'\n"); ret = -ENXIO; - goto fail_irq; + goto fail; } sdev->dac = clk_get(dev, "dac"); if (IS_ERR_OR_NULL(sdev->dac)) { @@ -415,12 +416,6 @@ fail_dac: clk_put(sdev->dac); fail_sclk_dac: clk_put(sdev->sclk_dac); -fail_irq: - free_irq(sdev->irq, sdev); -fail_regs: - iounmap(sdev->regs); -fail_sdev: - kfree(sdev); fail: dev_info(dev, "probe failed\n"); return ret; @@ -439,9 +434,6 @@ static int __devexit sdo_remove(struct platform_device *pdev) clk_put(sdev->dacphy); clk_put(sdev->dac); clk_put(sdev->sclk_dac); - free_irq(sdev->irq, sdev); - iounmap(sdev->regs); - kfree(sdev); dev_info(&pdev->dev, "remove successful\n"); return 0; diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c new file mode 100644 index 0000000..0f31ecc --- /dev/null +++ b/drivers/media/video/s5p-tv/sii9234_drv.c @@ -0,0 +1,432 @@ +/* + * Samsung MHL interface driver + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Tomasz Stanislawski <t.stanislaws@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/delay.h> +#include <linux/err.h> +#include <linux/freezer.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/machine.h> +#include <linux/slab.h> + +#include <mach/gpio.h> +#include <plat/gpio-cfg.h> + +#include <media/sii9234.h> +#include <media/v4l2-subdev.h> + +MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); +MODULE_DESCRIPTION("Samsung MHL interface driver"); +MODULE_LICENSE("GPL"); + +struct sii9234_context { + struct i2c_client *client; + struct regulator *power; + int gpio_n_reset; + struct v4l2_subdev sd; +}; + +static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sii9234_context, sd); +} + +static inline int sii9234_readb(struct i2c_client *client, int addr) +{ + return i2c_smbus_read_byte_data(client, addr); +} + +static inline int sii9234_writeb(struct i2c_client *client, int addr, int value) +{ + return i2c_smbus_write_byte_data(client, addr, value); +} + +static inline int sii9234_writeb_mask(struct i2c_client *client, int addr, + int value, int mask) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, addr); + if (ret < 0) + return ret; + ret = (ret & ~mask) | (value & mask); + return i2c_smbus_write_byte_data(client, addr, ret); +} + +static inline int sii9234_readb_idx(struct i2c_client *client, int addr) +{ + int ret; + ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); + if (ret < 0) + return ret; + return i2c_smbus_read_byte_data(client, 0xbe); +} + +static inline int sii9234_writeb_idx(struct i2c_client *client, int addr, + int value) +{ + int ret; + ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, 0xbe, value); + return ret; +} + +static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr, + int value, int mask) +{ + int ret; + + ret = sii9234_readb_idx(client, addr); + if (ret < 0) + return ret; + ret = (ret & ~mask) | (value & mask); + return sii9234_writeb_idx(client, addr, ret); +} + +static int sii9234_reset(struct sii9234_context *ctx) +{ + struct i2c_client *client = ctx->client; + struct device *dev = &client->dev; + int ret, tries; + + gpio_direction_output(ctx->gpio_n_reset, 1); + mdelay(1); + gpio_direction_output(ctx->gpio_n_reset, 0); + mdelay(1); + gpio_direction_output(ctx->gpio_n_reset, 1); + mdelay(1); + + /* going to TTPI mode */ + ret = sii9234_writeb(client, 0xc7, 0); + if (ret < 0) { + dev_err(dev, "failed to set TTPI mode\n"); + return ret; + } + for (tries = 0; tries < 100 ; ++tries) { + ret = sii9234_readb(client, 0x1b); + if (ret > 0) + break; + if (ret < 0) { + dev_err(dev, "failed to reset device\n"); + return -EIO; + } + mdelay(1); + } + if (tries == 100) { + dev_err(dev, "maximal number of tries reached\n"); + return -EIO; + } + + return 0; +} + +static int sii9234_verify_version(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int family, rev, tpi_rev, dev_id, sub_id, hdcp, id; + + family = sii9234_readb(client, 0x1b); + rev = sii9234_readb(client, 0x1c) & 0x0f; + tpi_rev = sii9234_readb(client, 0x1d) & 0x7f; + dev_id = sii9234_readb_idx(client, 0x0103); + sub_id = sii9234_readb_idx(client, 0x0102); + hdcp = sii9234_readb(client, 0x30); + + if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 || + sub_id < 0 || hdcp < 0) { + dev_err(dev, "failed to read chip's version\n"); + return -EIO; + } + + id = (dev_id << 8) | sub_id; + + dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n", + id, family, rev); + dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp); + if (id != 0x9234) { + dev_err(dev, "not supported chip\n"); + return -ENODEV; + } + + return 0; +} + +static u8 data[][3] = { +/* setup from driver created by doonsoo45.kim */ + { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */ + { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */ + { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */ + { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */ + { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */ + { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */ + { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */ + { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */ + { 0x01, 0x92, 0x46 }, /* Force MHD mode */ + { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */ + { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */ + { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */ + { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */ + { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */ + { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */ + { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */ + { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */ + { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */ + { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */ + { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */ + { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */ + { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */ + { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */ + { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */ + { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */ + { 0x03, 0x1a, 0x20 }, /* VCO Cal */ + { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */ + { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */ + { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */ + { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */ + { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */ + { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */ + { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */ + { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */ + { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */ + { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */ + { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */ + { 0x03, 0x4c, 0xa0 }, /* Manual zone control */ + { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */ +}; + +static int sii9234_set_internal(struct sii9234_context *ctx) +{ + struct i2c_client *client = ctx->client; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(data); ++i) { + int addr = (data[i][0] << 8) | data[i][1]; + ret = sii9234_writeb_idx(client, addr, data[i][2]); + if (ret < 0) + return ret; + } + return 0; +} + +static int sii9234_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct sii9234_context *ctx = sd_to_context(sd); + struct i2c_client *client = ctx->client; + + dev_info(dev, "suspend start\n"); + + sii9234_writeb_mask(client, 0x1e, 3, 3); + regulator_disable(ctx->power); + + return 0; +} + +static int sii9234_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct sii9234_context *ctx = sd_to_context(sd); + struct i2c_client *client = ctx->client; + int ret; + + dev_info(dev, "resume start\n"); + regulator_enable(ctx->power); + + ret = sii9234_reset(ctx); + if (ret) + goto fail; + + /* enable tpi */ + ret = sii9234_writeb_mask(client, 0x1e, 1, 0); + if (ret < 0) + goto fail; + ret = sii9234_set_internal(ctx); + if (ret < 0) + goto fail; + + return 0; + +fail: + dev_err(dev, "failed to resume\n"); + regulator_disable(ctx->power); + + return ret; +} + +static const struct dev_pm_ops sii9234_pm_ops = { + .runtime_suspend = sii9234_runtime_suspend, + .runtime_resume = sii9234_runtime_resume, +}; + +static int sii9234_s_power(struct v4l2_subdev *sd, int on) +{ + struct sii9234_context *ctx = sd_to_context(sd); + int ret; + + if (on) + ret = pm_runtime_get_sync(&ctx->client->dev); + else + ret = pm_runtime_put(&ctx->client->dev); + /* only values < 0 indicate errors */ + return IS_ERR_VALUE(ret) ? ret : 0; +} + +static int sii9234_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct sii9234_context *ctx = sd_to_context(sd); + + /* (dis/en)able TDMS output */ + sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4); + return 0; +} + +static const struct v4l2_subdev_core_ops sii9234_core_ops = { + .s_power = sii9234_s_power, +}; + +static const struct v4l2_subdev_video_ops sii9234_video_ops = { + .s_stream = sii9234_s_stream, +}; + +static const struct v4l2_subdev_ops sii9234_ops = { + .core = &sii9234_core_ops, + .video = &sii9234_video_ops, +}; + +static int __devinit sii9234_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct sii9234_platform_data *pdata = dev->platform_data; + struct sii9234_context *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + dev_err(dev, "out of memory\n"); + ret = -ENOMEM; + goto fail; + } + ctx->client = client; + + ctx->power = regulator_get(dev, "hdmi-en"); + if (IS_ERR(ctx->power)) { + dev_err(dev, "failed to acquire regulator hdmi-en\n"); + ret = PTR_ERR(ctx->power); + goto fail_ctx; + } + + ctx->gpio_n_reset = pdata->gpio_n_reset; + ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); + if (ret) { + dev_err(dev, "failed to acquire MHL_RST gpio\n"); + goto fail_power; + } + + v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops); + + pm_runtime_enable(dev); + + /* enable device */ + ret = pm_runtime_get_sync(dev); + if (ret) + goto fail_pm; + + /* verify chip version */ + ret = sii9234_verify_version(client); + if (ret) + goto fail_pm_get; + + /* stop processing */ + pm_runtime_put(dev); + + dev_info(dev, "probe successful\n"); + + return 0; + +fail_pm_get: + pm_runtime_put_sync(dev); + +fail_pm: + pm_runtime_disable(dev); + gpio_free(ctx->gpio_n_reset); + +fail_power: + regulator_put(ctx->power); + +fail_ctx: + kfree(ctx); + +fail: + dev_err(dev, "probe failed\n"); + + return ret; +} + +static int __devexit sii9234_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sii9234_context *ctx = sd_to_context(sd); + + pm_runtime_disable(dev); + gpio_free(ctx->gpio_n_reset); + regulator_put(ctx->power); + kfree(ctx); + + dev_info(dev, "remove successful\n"); + + return 0; +} + + +static const struct i2c_device_id sii9234_id[] = { + { "SII9234", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, sii9234_id); +static struct i2c_driver sii9234_driver = { + .driver = { + .name = "sii9234", + .owner = THIS_MODULE, + .pm = &sii9234_pm_ops, + }, + .probe = sii9234_probe, + .remove = __devexit_p(sii9234_remove), + .id_table = sii9234_id, +}; + +static int __init sii9234_init(void) +{ + return i2c_add_driver(&sii9234_driver); +} +module_init(sii9234_init); + +static void __exit sii9234_exit(void) +{ + i2c_del_driver(&sii9234_driver); +} +module_exit(sii9234_exit); diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index 99a2ac1..0caac50 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -539,15 +539,4 @@ static struct i2c_driver saa6588_driver = { .id_table = saa6588_id, }; -static __init int init_saa6588(void) -{ - return i2c_add_driver(&saa6588_driver); -} - -static __exit void exit_saa6588(void) -{ - i2c_del_driver(&saa6588_driver); -} - -module_init(init_saa6588); -module_exit(exit_saa6588); +module_i2c_driver(saa6588_driver); diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 9966420..51cd4c8 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -491,15 +491,4 @@ static struct i2c_driver saa7110_driver = { .id_table = saa7110_id, }; -static __init int init_saa7110(void) -{ - return i2c_add_driver(&saa7110_driver); -} - -static __exit void exit_saa7110(void) -{ - i2c_del_driver(&saa7110_driver); -} - -module_init(init_saa7110); -module_exit(exit_saa7110); +module_i2c_driver(saa7110_driver); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 0ef5484..2107336 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1724,15 +1724,4 @@ static struct i2c_driver saa711x_driver = { .id_table = saa711x_id, }; -static __init int init_saa711x(void) -{ - return i2c_add_driver(&saa711x_driver); -} - -static __exit void exit_saa711x(void) -{ - i2c_del_driver(&saa711x_driver); -} - -module_init(init_saa711x); -module_exit(exit_saa711x); +module_i2c_driver(saa711x_driver); diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index ad96461..39c90b0 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -852,15 +852,4 @@ static struct i2c_driver saa7127_driver = { .id_table = saa7127_id, }; -static __init int init_saa7127(void) -{ - return i2c_add_driver(&saa7127_driver); -} - -static __exit void exit_saa7127(void) -{ - i2c_del_driver(&saa7127_driver); -} - -module_init(init_saa7127); -module_exit(exit_saa7127); +module_i2c_driver(saa7127_driver); diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index a646ccf..da38993 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index f9f29cc..f147b05 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -1001,18 +1001,7 @@ static struct i2c_driver saa6752hs_driver = { .id_table = saa6752hs_id, }; -static __init int init_saa6752hs(void) -{ - return i2c_add_driver(&saa6752hs_driver); -} - -static __exit void exit_saa6752hs(void) -{ - i2c_del_driver(&saa6752hs_driver); -} - -module_init(init_saa6752hs); -module_exit(exit_saa6752hs); +module_i2c_driver(saa6752hs_driver); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 065d0f6..53aae59 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -33,6 +33,7 @@ #include "tea5767.h" #include "tda18271.h" #include "xc5000.h" +#include "s5h1411.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -5712,6 +5713,36 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, } }, }, + [SAA7134_BOARD_KWORLD_PC150U] = { + .name = "Kworld PC150-U", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 1 << 21, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0000000, + }, + }, }; @@ -6306,6 +6337,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */ },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x17de, + .subdevice = 0xa134, + .driver_data = SAA7134_BOARD_KWORLD_PC150U, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x1461, .subdevice = 0x7360, @@ -7134,6 +7171,23 @@ static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev, return 0; } +static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 18, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 18, 1); + msleep(30); + break; + default: + return -EINVAL; + } + return 0; +} + static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, int command, int arg) { @@ -7150,6 +7204,9 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg); break; + case SAA7134_BOARD_KWORLD_PC150U: + ret = saa7134_kworld_pc150u_toggle_agc(dev, arg); + break; default: break; } @@ -7171,6 +7228,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_AVERMEDIA_M733A: case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + case SAA7134_BOARD_KWORLD_PC150U: case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); @@ -7452,6 +7510,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_X7: case SAA7134_BOARD_BEHOLD_H7: case SAA7134_BOARD_BEHOLD_A7: + case SAA7134_BOARD_KWORLD_PC150U: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 089fa0f..aaa5c97 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -61,6 +61,7 @@ #include "zl10036.h" #include "zl10039.h" #include "mt312.h" +#include "s5h1411.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -1158,6 +1159,33 @@ static struct tda18271_config prohdtv_pro2_tda18271_config = { .output_opt = TDA18271_OUTPUT_LT_OFF, }; +static struct tda18271_std_map kworld_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37 }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37 }, +}; + +static struct tda18271_config kworld_pc150u_tda18271_config = { + .std_map = &kworld_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, + .config = 3, /* Use tuner callback for AGC */ + .rf_cal_on_startup = 1 +}; + +static struct s5h1411_config kworld_s5h1411_config = { + .output_mode = S5H1411_PARALLEL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .qam_if = S5H1411_IF_4000, + .vsb_if = S5H1411_IF_3250, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = + S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + + /* ================================================================== * Core code */ @@ -1438,6 +1466,22 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0x61, TUNER_PHILIPS_TUV1236D); break; + case SAA7134_BOARD_KWORLD_PC150U: + saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */ + saa7134_tuner_callback(dev, 0, + TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &kworld_s5h1411_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &kworld_pc150u_tda18271_config); + } + break; case SAA7134_BOARD_FLYDVBS_LR300: fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 2d3f6d2..a176ec3 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -254,7 +254,9 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, addr = msgs[i].addr << 1; if (msgs[i].flags & I2C_M_RD) addr |= 1; - if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40) { + if (i > 0 && msgs[i].flags & + I2C_M_RD && msgs[i].addr != 0x40 && + msgs[i].addr != 0x19) { /* workaround for a saa7134 i2c bug * needed to talk to the mt352 demux * thanks to pinnacle for the hint */ @@ -279,6 +281,16 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, d1printk("%02x", rc); msgs[i].buf[byte] = rc; } + /* discard mysterious extra byte when reading + from Samsung S5H1411. i2c bus gets error + if we do not. */ + if (0x19 == msgs[i].addr) { + d1printk(" ?"); + rc = i2c_recv_byte(dev); + if (rc < 0) + goto err; + d1printk("%02x", rc); + } } else { /* write bytes */ d2printk("write bytes\n"); diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 22ecd72..48d2878 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -210,6 +210,54 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, return 1; } +/* copied and modified from get_key_msi_tvanywhere_plus() */ +static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + unsigned char b; + unsigned int gpio; + + /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ + struct saa7134_dev *dev = ir->c->adapter->algo_data; + if (dev == NULL) { + i2cdprintk("get_key_kworld_pc150u: " + "ir->c->adapter->algo_data is NULL!\n"); + return -EIO; + } + + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + + /* GPIO&0x100 is pulsed low when a button is pressed. Don't do + I2C receive if gpio&0x100 is not low. */ + + if (gpio & 0x100) + return 0; /* No button press */ + + /* GPIO says there is a button press. Get it. */ + + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + /* No button press */ + + if (b == 0xff) + return 0; + + /* Button pressed */ + + dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); + *ir_key = b; + *ir_raw = b; + return 1; +} + static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -901,6 +949,21 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) msg_msi.addr, dev->i2c_adap.name, (1 == rc) ? "yes" : "no"); break; + case SAA7134_BOARD_KWORLD_PC150U: + /* copied and modified from MSI TV@nywhere Plus */ + dev->init_data.name = "Kworld PC150-U"; + dev->init_data.get_key = get_key_kworld_pc150u; + dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U; + info.addr = 0x30; + /* MSI TV@nywhere Plus controller doesn't seem to + respond to probes unless we read something from + an existing device. Weird... + REVISIT: might no longer be needed */ + rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); + dprintk("probe 0x%02x @ %s: %s\n", + msg_msi.addr, dev->i2c_adap.name, + (1 == rc) ? "yes" : "no"); + break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 42fba4f..f625060 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -126,8 +126,8 @@ struct saa7134_card_ir { unsigned users; u32 polling; - u32 last_gpio; - u32 mask_keycode, mask_keydown, mask_keyup; + u32 last_gpio; + u32 mask_keycode, mask_keydown, mask_keyup; bool running; bool active; @@ -331,6 +331,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_BEHOLD_501 186 #define SAA7134_BOARD_BEHOLD_503FM 187 #define SAA7134_BOARD_SENSORAY811_911 188 +#define SAA7134_BOARD_KWORLD_PC150U 189 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile index ecd5811..068443a 100644 --- a/drivers/media/video/saa7164/Makefile +++ b/drivers/media/video/saa7164/Makefile @@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o -ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners -ccflags-y += -Idrivers/media/dvb/dvb-core -ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -I$(srctree)/drivers/media/video +ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb/frontends ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c index 2fd38a0..a9ed686 100644 --- a/drivers/media/video/saa7164/saa7164-encoder.c +++ b/drivers/media/video/saa7164/saa7164-encoder.c @@ -791,11 +791,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_log_status(struct file *file, void *priv) -{ - return 0; -} - static int fill_queryctrl(struct saa7164_encoder_params *params, struct v4l2_queryctrl *c) { @@ -1347,7 +1342,6 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_log_status = vidioc_log_status, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_chip_ident = saa7164_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c index e2e0341..273cf80 100644 --- a/drivers/media/video/saa7164/saa7164-vbi.c +++ b/drivers/media/video/saa7164/saa7164-vbi.c @@ -730,11 +730,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_log_status(struct file *file, void *priv) -{ - return 0; -} - static int fill_queryctrl(struct saa7164_vbi_params *params, struct v4l2_queryctrl *c) { @@ -1256,7 +1251,6 @@ static const struct v4l2_ioctl_ops vbi_ioctl_ops = { .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_log_status = vidioc_log_status, .vidioc_queryctrl = vidioc_queryctrl, #if 0 .vidioc_g_chip_ident = saa7164_g_chip_ident, diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index b6172c2..1e84466 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1375,15 +1375,4 @@ static struct i2c_driver saa717x_driver = { .id_table = saa717x_id, }; -static __init int init_saa717x(void) -{ - return i2c_add_driver(&saa717x_driver); -} - -static __exit void exit_saa717x(void) -{ - i2c_del_driver(&saa717x_driver); -} - -module_init(init_saa717x); -module_exit(exit_saa717x); +module_i2c_driver(saa717x_driver); diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c index 96f56c2..2c6b65c 100644 --- a/drivers/media/video/saa7185.c +++ b/drivers/media/video/saa7185.c @@ -374,15 +374,4 @@ static struct i2c_driver saa7185_driver = { .id_table = saa7185_id, }; -static __init int init_saa7185(void) -{ - return i2c_add_driver(&saa7185_driver); -} - -static __exit void exit_saa7185(void) -{ - i2c_del_driver(&saa7185_driver); -} - -module_init(init_saa7185); -module_exit(exit_saa7185); +module_i2c_driver(saa7185_driver); diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c index 211fa25..d7d1670 100644 --- a/drivers/media/video/saa7191.c +++ b/drivers/media/video/saa7191.c @@ -656,15 +656,4 @@ static struct i2c_driver saa7191_driver = { .id_table = saa7191_id, }; -static __init int init_saa7191(void) -{ - return i2c_add_driver(&saa7191_driver); -} - -static __exit void exit_saa7191(void) -{ - i2c_del_driver(&saa7191_driver); -} - -module_init(init_saa7191); -module_exit(exit_saa7191); +module_i2c_driver(saa7191_driver); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index f854d85..424dfac 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -112,6 +112,10 @@ struct sh_mobile_ceu_dev { u32 cflcr; + /* static max sizes either from platform data or default */ + int max_width; + int max_height; + enum v4l2_field field; int sequence; @@ -1081,7 +1085,15 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int if (ret < 0) return ret; - while ((mf.width > 2560 || mf.height > 1920) && shift < 4) { + /* + * All currently existing CEU implementations support 2560x1920 + * or larger frames. If the sensor is proposing too big a frame, + * don't bother with possibly supportred by the CEU larger + * sizes, just try VGA multiples. If needed, this can be + * adjusted in the future. + */ + while ((mf.width > pcdev->max_width || + mf.height > pcdev->max_height) && shift < 4) { /* Try 2560x1920, 1280x960, 640x480, 320x240 */ mf.width = 2560 >> shift; mf.height = 1920 >> shift; @@ -1377,6 +1389,8 @@ static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; @@ -1410,8 +1424,8 @@ static int client_s_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; - max_width = min(cap.bounds.width, 2560); - max_height = min(cap.bounds.height, 1920); + max_width = min(cap.bounds.width, pcdev->max_width); + max_height = min(cap.bounds.height, pcdev->max_height); /* Camera set a format, but geometry is not precise, try to improve */ tmp_w = mf->width; @@ -1551,7 +1565,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, if (ret < 0) return ret; - if (mf.width > 2560 || mf.height > 1920) + if (mf.width > pcdev->max_width || mf.height > pcdev->max_height) return -EINVAL; /* 4. Calculate camera scales */ @@ -1834,6 +1848,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -1854,8 +1870,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, /* FIXME: calculate using depth and bus width */ /* CFSZR requires height and width to be 4-pixel aligned */ - v4l_bound_align_image(&pix->width, 2, 2560, 2, - &pix->height, 4, 1920, 2, 0); + v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2, + &pix->height, 4, pcdev->max_height, 2, 0); width = pix->width; height = pix->height; @@ -1890,8 +1906,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, * requested a bigger rectangle, it will not return a * smaller one. */ - mf.width = 2560; - mf.height = 1920; + mf.width = pcdev->max_width; + mf.height = pcdev->max_height; ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), video, try_mbus_fmt, &mf); @@ -2082,6 +2098,9 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_kfree; } + pcdev->max_width = pcdev->pdata->max_width ? : 2560; + pcdev->max_height = pcdev->pdata->max_height ? : 1920; + base = ioremap_nocache(res->start, resource_size(res)); if (!base) { err = -ENXIO; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index b827107..eb25756 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -526,10 +526,6 @@ static int soc_camera_open(struct file *file) }, }; - ret = soc_camera_power_on(icd, icl); - if (ret < 0) - goto epower; - /* The camera could have been already on, try to reset */ if (icl->reset) icl->reset(icd->pdev); @@ -540,6 +536,10 @@ static int soc_camera_open(struct file *file) goto eiciadd; } + ret = soc_camera_power_on(icd, icl); + if (ret < 0) + goto epower; + pm_runtime_enable(&icd->vdev->dev); ret = pm_runtime_resume(&icd->vdev->dev); if (ret < 0 && ret != -ENOSYS) @@ -578,10 +578,10 @@ einitvb: esfmt: pm_runtime_disable(&icd->vdev->dev); eresume: - ici->ops->remove(icd); -eiciadd: soc_camera_power_off(icd, icl); epower: + ici->ops->remove(icd); +eiciadd: icd->use_count--; module_put(ici->ops->owner); @@ -1050,6 +1050,14 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) goto ereg; + /* The camera could have been already on, try to reset */ + if (icl->reset) + icl->reset(icd->pdev); + + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + /* * This will not yet call v4l2_subdev_core_ops::s_power(1), because the * subdevice has not been initialised yet. We'll have to call it once @@ -1060,14 +1068,6 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) goto epower; - /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); - - ret = ici->ops->add(icd); - if (ret < 0) - goto eadd; - /* Must have icd->vdev before registering the device */ ret = video_dev_create(icd); if (ret < 0) @@ -1165,10 +1165,10 @@ eadddev: video_device_release(icd->vdev); icd->vdev = NULL; evdc: - ici->ops->remove(icd); -eadd: soc_camera_power_off(icd, icl); epower: + ici->ops->remove(icd); +eadd: regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c index d1b07ac..e9d95bd 100644 --- a/drivers/media/video/sr030pc30.c +++ b/drivers/media/video/sr030pc30.c @@ -864,18 +864,7 @@ static struct i2c_driver sr030pc30_i2c_driver = { .id_table = sr030pc30_id, }; -static int __init sr030pc30_init(void) -{ - return i2c_add_driver(&sr030pc30_i2c_driver); -} - -static void __exit sr030pc30_exit(void) -{ - i2c_del_driver(&sr030pc30_i2c_driver); -} - -module_init(sr030pc30_init); -module_exit(sr030pc30_exit); +module_i2c_driver(sr030pc30_i2c_driver); MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index bd21854..f7707e6 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -482,15 +482,4 @@ static struct i2c_driver tda7432_driver = { .id_table = tda7432_id, }; -static __init int init_tda7432(void) -{ - return i2c_add_driver(&tda7432_driver); -} - -static __exit void exit_tda7432(void) -{ - i2c_del_driver(&tda7432_driver); -} - -module_init(init_tda7432); -module_exit(exit_tda7432); +module_i2c_driver(tda7432_driver); diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 22fa820..465d708 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -208,15 +208,4 @@ static struct i2c_driver tda9840_driver = { .id_table = tda9840_id, }; -static __init int init_tda9840(void) -{ - return i2c_add_driver(&tda9840_driver); -} - -static __exit void exit_tda9840(void) -{ - i2c_del_driver(&tda9840_driver); -} - -module_init(init_tda9840); -module_exit(exit_tda9840); +module_i2c_driver(tda9840_driver); diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index 827425c..d1d6ea1 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -184,15 +184,4 @@ static struct i2c_driver tea6415c_driver = { .id_table = tea6415c_id, }; -static __init int init_tea6415c(void) -{ - return i2c_add_driver(&tea6415c_driver); -} - -static __exit void exit_tea6415c(void) -{ - i2c_del_driver(&tea6415c_driver); -} - -module_init(init_tea6415c); -module_exit(exit_tea6415c); +module_i2c_driver(tea6415c_driver); diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index f350b6c..3875721 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -166,15 +166,4 @@ static struct i2c_driver tea6420_driver = { .id_table = tea6420_id, }; -static __init int init_tea6420(void) -{ - return i2c_add_driver(&tea6420_driver); -} - -static __exit void exit_tea6420(void) -{ - i2c_del_driver(&tea6420_driver); -} - -module_init(init_tea6420); -module_exit(exit_tea6420); +module_i2c_driver(tea6420_driver); diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c index 61b1dd1..e5c0eed 100644 --- a/drivers/media/video/ths7303.c +++ b/drivers/media/video/ths7303.c @@ -137,16 +137,4 @@ static struct i2c_driver ths7303_driver = { .id_table = ths7303_id, }; -static int __init ths7303_init(void) -{ - return i2c_add_driver(&ths7303_driver); -} - -static void __exit ths7303_exit(void) -{ - i2c_del_driver(&ths7303_driver); -} - -module_init(ths7303_init); -module_exit(ths7303_exit); - +module_i2c_driver(ths7303_driver); diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c index 4ed1c7c2..02194c0 100644 --- a/drivers/media/video/timblogiw.c +++ b/drivers/media/video/timblogiw.c @@ -564,7 +564,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) spin_unlock_irq(&fh->queue_lock); - desc = fh->chan->device->device_prep_slave_sg(fh->chan, + desc = dmaengine_prep_slave_sg(fh->chan, buf->sg, sg_elems, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); if (!desc) { diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index 286ec7e..809a75a 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -227,15 +227,4 @@ static struct i2c_driver tlv320aic23b_driver = { .id_table = tlv320aic23b_id, }; -static __init int init_tlv320aic23b(void) -{ - return i2c_add_driver(&tlv320aic23b_driver); -} - -static __exit void exit_tlv320aic23b(void) -{ - i2c_del_driver(&tlv320aic23b_driver); -} - -module_init(init_tlv320aic23b); -module_exit(exit_tlv320aic23b); +module_i2c_driver(tlv320aic23b_driver); diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c index 7844607..859eb90 100644 --- a/drivers/media/video/tm6000/tm6000-input.c +++ b/drivers/media/video/tm6000/tm6000-input.c @@ -481,8 +481,6 @@ int tm6000_ir_fini(struct tm6000_core *dev) dprintk(2, "%s\n",__func__); - rc_unregister_device(ir->rc); - if (!ir->polling) __tm6000_ir_int_stop(ir->rc); @@ -492,6 +490,7 @@ int tm6000_ir_fini(struct tm6000_core *dev) tm6000_flash_led(dev, 0); ir->pwled = 0; + rc_unregister_device(ir->rc); kfree(ir); dev->ir = NULL; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 4059ea17..a5c6397 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -380,6 +380,21 @@ static void set_type(struct i2c_client *c, unsigned int type, tune_now = 0; break; } + case TUNER_XC5000C: + { + struct xc5000_config xc5000c_cfg = { + .i2c_address = t->i2c->addr, + /* if_khz will be set at dvb_attach() */ + .if_khz = 0, + .chip_id = XC5000C, + }; + + if (!dvb_attach(xc5000_attach, + &t->fe, t->i2c->adapter, &xc5000c_cfg)) + goto attach_failed; + tune_now = 0; + break; + } case TUNER_NXP_TDA18271: { struct tda18271_config cfg = { @@ -1314,18 +1329,7 @@ static struct i2c_driver tuner_driver = { .id_table = tuner_id, }; -static __init int init_tuner(void) -{ - return i2c_add_driver(&tuner_driver); -} - -static __exit void exit_tuner(void) -{ - i2c_del_driver(&tuner_driver); -} - -module_init(init_tuner); -module_exit(exit_tuner); +module_i2c_driver(tuner_driver); MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index f22dbef..c5b1a73 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -2078,15 +2078,4 @@ static struct i2c_driver tvaudio_driver = { .id_table = tvaudio_id, }; -static __init int init_tvaudio(void) -{ - return i2c_add_driver(&tvaudio_driver); -} - -static __exit void exit_tvaudio(void) -{ - i2c_del_driver(&tvaudio_driver); -} - -module_init(init_tvaudio); -module_exit(exit_tvaudio); +module_i2c_driver(tvaudio_driver); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 6103d1b..3b6cf03 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -286,8 +286,16 @@ hauppauge_tuner[] = { TUNER_ABSENT, "MaxLinear 301"}, { TUNER_ABSENT, "Mirics MSi001"}, { TUNER_ABSENT, "MaxLinear MxL241SF"}, - { TUNER_ABSENT, "Xceive XC5000C"}, + { TUNER_XC5000C, "Xceive XC5000C"}, { TUNER_ABSENT, "Montage M68TS2020"}, + { TUNER_ABSENT, "Siano SMS1530"}, + { TUNER_ABSENT, "Dibcom 7090"}, + { TUNER_ABSENT, "Xceive XC5200C"}, + { TUNER_ABSENT, "NXP 18273"}, + { TUNER_ABSENT, "Montage M88TS2022"}, + /* 180-189 */ + { TUNER_ABSENT, "NXP 18272M"}, + { TUNER_ABSENT, "NXP 18272S"}, }; /* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index dd26cac..cd615c1 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -1163,15 +1163,4 @@ static struct i2c_driver tvp514x_driver = { .id_table = tvp514x_id, }; -static int __init tvp514x_init(void) -{ - return i2c_add_driver(&tvp514x_driver); -} - -static void __exit tvp514x_exit(void) -{ - i2c_del_driver(&tvp514x_driver); -} - -module_init(tvp514x_init); -module_exit(tvp514x_exit); +module_i2c_driver(tvp514x_driver); diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 6be9910..1326e11 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -17,6 +17,13 @@ #include "tvp5150_reg.h" +#define TVP5150_H_MAX 720 +#define TVP5150_V_MAX_525_60 480 +#define TVP5150_V_MAX_OTHERS 576 +#define TVP5150_MAX_CROP_LEFT 511 +#define TVP5150_MAX_CROP_TOP 127 +#define TVP5150_CROP_SHIFT 2 + MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); @@ -29,6 +36,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); struct tvp5150 { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; + struct v4l2_rect rect; v4l2_std_id norm; /* Current set standard */ u32 input; @@ -732,6 +740,13 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) if (decoder->norm == std) return 0; + /* Change cropping height limits */ + if (std & V4L2_STD_525_60) + decoder->rect.height = TVP5150_V_MAX_525_60; + else + decoder->rect.height = TVP5150_V_MAX_OTHERS; + + return tvp5150_set_std(sd, std); } @@ -828,11 +843,8 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, else std = decoder->norm; - f->width = 720; - if (std & V4L2_STD_525_60) - f->height = 480; - else - f->height = 576; + f->width = decoder->rect.width; + f->height = decoder->rect.height; f->code = V4L2_MBUS_FMT_YUYV8_2X8; f->field = V4L2_FIELD_SEQ_TB; @@ -843,6 +855,99 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, return 0; } +static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect rect = a->c; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + int hmax; + + v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect.left, rect.top, rect.width, rect.height); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* tvp5150 has some special limits */ + rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); + rect.width = clamp(rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left); + rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + hmax = TVP5150_V_MAX_525_60; + else + hmax = TVP5150_V_MAX_OTHERS; + + rect.height = clamp(rect.height, + hmax - TVP5150_MAX_CROP_TOP - rect.top, + hmax - rect.top); + + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, + rect.top + rect.height - hmax); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, + rect.left >> TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, + rect.left | (1 << TVP5150_CROP_SHIFT)); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, + (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> + TVP5150_CROP_SHIFT); + tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, + rect.left + rect.width - TVP5150_MAX_CROP_LEFT); + + decoder->rect = rect; + + return 0; +} + +static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + + a->c = decoder->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + v4l2_std_id std; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = TVP5150_H_MAX; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + if (std & V4L2_STD_525_60) + a->bounds.height = TVP5150_V_MAX_525_60; + else + a->bounds.height = TVP5150_V_MAX_OTHERS; + + a->defrect = a->bounds; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + /**************************************************************************** I2C Command ****************************************************************************/ @@ -998,6 +1103,10 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .enum_mbus_fmt = tvp5150_enum_mbus_fmt, .s_mbus_fmt = tvp5150_mbus_fmt, .try_mbus_fmt = tvp5150_mbus_fmt, + .g_mbus_fmt = tvp5150_mbus_fmt, + .s_crop = tvp5150_s_crop, + .g_crop = tvp5150_g_crop, + .cropcap = tvp5150_cropcap, }; static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { @@ -1083,6 +1192,15 @@ static int tvp5150_probe(struct i2c_client *c, } v4l2_ctrl_handler_setup(&core->hdl); + /* Default is no cropping */ + core->rect.top = 0; + if (tvp5150_read_std(sd) & V4L2_STD_525_60) + core->rect.height = TVP5150_V_MAX_525_60; + else + core->rect.height = TVP5150_V_MAX_OTHERS; + core->rect.left = 0; + core->rect.width = TVP5150_H_MAX; + if (debug > 1) tvp5150_log_status(sd); return 0; @@ -1121,15 +1239,4 @@ static struct i2c_driver tvp5150_driver = { .id_table = tvp5150_id, }; -static __init int init_tvp5150(void) -{ - return i2c_add_driver(&tvp5150_driver); -} - -static __exit void exit_tvp5150(void) -{ - i2c_del_driver(&tvp5150_driver); -} - -module_init(init_tvp5150); -module_exit(exit_tvp5150); +module_i2c_driver(tvp5150_driver); diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 236c559..d7676d8 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -1069,27 +1069,4 @@ static struct i2c_driver tvp7002_driver = { .id_table = tvp7002_id, }; -/* - * tvp7002_init - Initialize driver via I2C interface - * - * Register the TVP7002 driver. - * Return 0 on success or error code on failure. - */ -static int __init tvp7002_init(void) -{ - return i2c_add_driver(&tvp7002_driver); -} - -/* - * tvp7002_exit - Remove driver via I2C interface - * - * Unregister the TVP7002 driver. - * Returns nothing. - */ -static void __exit tvp7002_exit(void) -{ - i2c_del_driver(&tvp7002_driver); -} - -module_init(tvp7002_init); -module_exit(tvp7002_exit); +module_i2c_driver(tvp7002_driver); diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index a514fa6..8768efb 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -951,21 +951,7 @@ static struct i2c_driver tw9910_i2c_driver = { .id_table = tw9910_id, }; -/* - * module function - */ -static int __init tw9910_module_init(void) -{ - return i2c_add_driver(&tw9910_i2c_driver); -} - -static void __exit tw9910_module_exit(void) -{ - i2c_del_driver(&tw9910_i2c_driver); -} - -module_init(tw9910_module_init); -module_exit(tw9910_module_exit); +module_i2c_driver(tw9910_i2c_driver); MODULE_DESCRIPTION("SoC Camera driver for tw9910"); MODULE_AUTHOR("Kuninori Morimoto"); diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index 1aab96a..1e74465 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -271,15 +271,4 @@ static struct i2c_driver upd64031a_driver = { .id_table = upd64031a_id, }; -static __init int init_upd64031a(void) -{ - return i2c_add_driver(&upd64031a_driver); -} - -static __exit void exit_upd64031a(void) -{ - i2c_del_driver(&upd64031a_driver); -} - -module_init(init_upd64031a); -module_exit(exit_upd64031a); +module_i2c_driver(upd64031a_driver); diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 65d065a..75d6acc 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -243,15 +243,4 @@ static struct i2c_driver upd64083_driver = { .id_table = upd64083_id, }; -static __init int init_upd64083(void) -{ - return i2c_add_driver(&upd64083_driver); -} - -static __exit void exit_upd64083(void) -{ - i2c_del_driver(&upd64083_driver); -} - -module_init(init_upd64083); -module_exit(exit_upd64083); +module_i2c_driver(upd64083_driver); diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index a240d43..1d13172 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -23,6 +23,7 @@ * codec can't handle MJPEG data. */ +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> @@ -32,7 +33,6 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/version.h> -#include <asm/atomic.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> @@ -2139,6 +2139,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Dell XPS m1530 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05a9, + .idProduct = 0x2640, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Apple Built-In iSight */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 518f77d..8f54e24 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -126,7 +126,7 @@ void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, int drop_corrupted) { queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 2ae4f88..ff2cddd 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -11,6 +11,7 @@ * */ +#include <linux/compat.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/list.h> @@ -1012,7 +1013,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) default: uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); - return -EINVAL; + return -ENOTTY; } return ret; @@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file, return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } +#ifdef CONFIG_COMPAT +struct uvc_xu_control_mapping32 { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + __u32 v4l2_type; + __u32 data_type; + + compat_caddr_t menu_info; + __u32 menu_count; + + __u32 reserved[4]; +}; + +static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, + const struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) || + __get_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + memset(kp->reserved, 0, sizeof(kp->reserved)); + + if (kp->menu_count == 0) { + kp->menu_info = NULL; + return 0; + } + + if (__get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus)); + if (kmenus == NULL) + return -EFAULT; + kp->menu_info = kmenus; + + if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, + struct uvc_xu_control_mapping32 __user *up) +{ + struct uvc_menu_info __user *umenus; + struct uvc_menu_info __user *kmenus = kp->menu_info; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || + __put_user(kp->menu_count, &up->menu_count)) + return -EFAULT; + + __clear_user(up->reserved, sizeof(up->reserved)); + + if (kp->menu_count == 0) + return 0; + + if (get_user(p, &up->menu_info)) + return -EFAULT; + umenus = compat_ptr(p); + if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) + return -EFAULT; + + return 0; +} + +struct uvc_xu_control_query32 { + __u8 unit; + __u8 selector; + __u8 query; + __u16 size; + compat_caddr_t data; +}; + +static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, + const struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + __copy_from_user(kp, up, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) { + kp->data = NULL; + return 0; + } + + if (__get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + kdata = compat_alloc_user_space(kp->size); + if (kdata == NULL) + return -EFAULT; + kp->data = kdata; + + if (copy_in_user(kdata, udata, kp->size)) + return -EFAULT; + + return 0; +} + +static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, + struct uvc_xu_control_query32 __user *up) +{ + u8 __user *udata; + u8 __user *kdata = kp->data; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + __copy_to_user(up, kp, offsetof(typeof(*up), data))) + return -EFAULT; + + if (kp->size == 0) + return 0; + + if (get_user(p, &up->data)) + return -EFAULT; + udata = compat_ptr(p); + if (!access_ok(VERIFY_READ, udata, kp->size)) + return -EFAULT; + + if (copy_in_user(udata, kdata, kp->size)) + return -EFAULT; + + return 0; +} + +#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) +#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) + +static long uvc_v4l2_compat_ioctl32(struct file *file, + unsigned int cmd, unsigned long arg) +{ + union { + struct uvc_xu_control_mapping xmap; + struct uvc_xu_control_query xqry; + } karg; + void __user *up = compat_ptr(arg); + mm_segment_t old_fs; + long ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP32: + cmd = UVCIOC_CTRL_MAP; + ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY32: + cmd = UVCIOC_CTRL_QUERY; + ret = uvc_v4l2_get_xu_query(&karg.xqry, up); + break; + + default: + return -ENOIOCTLCMD; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if (ret < 0) + return ret; + + switch (cmd) { + case UVCIOC_CTRL_MAP: + ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); + break; + + case UVCIOC_CTRL_QUERY: + ret = uvc_v4l2_put_xu_query(&karg.xqry, up); + break; + } + + return ret; +} +#endif + static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = { .open = uvc_v4l2_open, .release = uvc_v4l2_release, .unlocked_ioctl = uvc_v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = uvc_v4l2_compat_ioctl32, +#endif .read = uvc_v4l2_read, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index c7e69b8..b76b0ac 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -468,22 +468,30 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, spin_unlock_irqrestore(&stream->clock.lock, flags); } -static int uvc_video_clock_init(struct uvc_streaming *stream) +static void uvc_video_clock_reset(struct uvc_streaming *stream) { struct uvc_clock *clock = &stream->clock; - spin_lock_init(&clock->lock); clock->head = 0; clock->count = 0; - clock->size = 32; clock->last_sof = -1; clock->sof_offset = -1; +} + +static int uvc_video_clock_init(struct uvc_streaming *stream) +{ + struct uvc_clock *clock = &stream->clock; + + spin_lock_init(&clock->lock); + clock->size = 32; clock->samples = kmalloc(clock->size * sizeof(*clock->samples), GFP_KERNEL); if (clock->samples == NULL) return -ENOMEM; + uvc_video_clock_reset(stream); + return 0; } @@ -611,9 +619,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream, delta_stc = buf->pts - (1UL << 31); x1 = first->dev_stc - delta_stc; x2 = last->dev_stc - delta_stc; + if (x1 == x2) + goto done; + y1 = (first->dev_sof + 2048) << 16; y2 = (last->dev_sof + 2048) << 16; - if (y2 < y1) y2 += 2048 << 16; @@ -631,14 +641,16 @@ void uvc_video_clock_update(struct uvc_streaming *stream, x1, x2, y1, y2, clock->sof_offset); /* Second step, SOF to host clock conversion. */ - ts = timespec_sub(last->host_ts, first->host_ts); x1 = (uvc_video_clock_host_sof(first) + 2048) << 16; x2 = (uvc_video_clock_host_sof(last) + 2048) << 16; - y1 = NSEC_PER_SEC; - y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; - if (x2 < x1) x2 += 2048 << 16; + if (x1 == x2) + goto done; + + ts = timespec_sub(last->host_ts, first->host_ts); + y1 = NSEC_PER_SEC; + y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec; /* Interpolated and host SOF timestamps can wrap around at slightly * different times. Handle this by adding or removing 2048 to or from @@ -1420,8 +1432,6 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) if (free_buffers) uvc_free_urb_buffers(stream); - - uvc_video_clock_cleanup(stream); } /* @@ -1551,10 +1561,6 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) uvc_video_stats_start(stream); - ret = uvc_video_clock_init(stream); - if (ret < 0) - return ret; - if (intf->num_altsetting > 1) { struct usb_host_endpoint *best_ep = NULL; unsigned int best_psize = 3 * 1024; @@ -1679,6 +1685,8 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) stream->frozen = 0; + uvc_video_clock_reset(stream); + ret = uvc_commit_video(stream, &stream->ctrl); if (ret < 0) { uvc_queue_enable(&stream->queue, 0); @@ -1815,25 +1823,35 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) uvc_uninit_video(stream, 1); usb_set_interface(stream->dev->udev, stream->intfnum, 0); uvc_queue_enable(&stream->queue, 0); + uvc_video_clock_cleanup(stream); return 0; } - ret = uvc_queue_enable(&stream->queue, 1); + ret = uvc_video_clock_init(stream); if (ret < 0) return ret; + ret = uvc_queue_enable(&stream->queue, 1); + if (ret < 0) + goto error_queue; + /* Commit the streaming parameters. */ ret = uvc_commit_video(stream, &stream->ctrl); - if (ret < 0) { - uvc_queue_enable(&stream->queue, 0); - return ret; - } + if (ret < 0) + goto error_commit; ret = uvc_init_video(stream, GFP_KERNEL); - if (ret < 0) { - usb_set_interface(stream->dev->udev, stream->intfnum, 0); - uvc_queue_enable(&stream->queue, 0); - } + if (ret < 0) + goto error_video; + + return 0; + +error_video: + usb_set_interface(stream->dev->udev, stream->intfnum, 0); +error_commit: + uvc_queue_enable(&stream->queue, 0); +error_queue: + uvc_video_clock_cleanup(stream); return ret; } diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 5c6100f..1baec83 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -55,7 +55,6 @@ #include <linux/spi/spi.h> #endif #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/pgtable.h> #include <asm/io.h> #include <asm/div64.h> diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index af4419e..2829d25 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -14,12 +14,11 @@ */ #include <linux/compat.h> -#include <linux/videodev2.h> #include <linux/module.h> +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> -#ifdef CONFIG_COMPAT - static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret = -ENOIOCTLCMD; @@ -937,6 +936,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { + struct video_device *vdev = video_devdata(file); long ret = -ENOIOCTLCMD; if (!file->f_op->unlocked_ioctl) @@ -1005,6 +1005,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_ENC_INDEX: case VIDIOC_ENCODER_CMD: case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_DECODER_CMD: + case VIDIOC_TRY_DECODER_CMD: case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: case VIDIOC_DBG_G_CHIP_IDENT: @@ -1025,14 +1027,16 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) break; default: - printk(KERN_WARNING "compat_ioctl32: " - "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", - _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd); + if (vdev->fops->compat_ioctl32) + ret = vdev->fops->compat_ioctl32(file, cmd, arg); + + if (ret == -ENOIOCTLCMD) + printk(KERN_WARNING "compat_ioctl32: " + "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", + _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), + cmd); break; } return ret; } EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32); -#endif - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index cccd42b..18015c0 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -175,6 +175,15 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "16-bit CRC", NULL }; + static const char * const mpeg_audio_dec_playback[] = { + "Auto", + "Stereo", + "Left", + "Right", + "Mono", + "Swapped Stereo", + NULL + }; static const char * const mpeg_video_encoding[] = { "MPEG-1", "MPEG-2", @@ -236,8 +245,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) }; static const char * const tune_preemphasis[] = { "No Preemphasis", - "50 useconds", - "75 useconds", + "50 Microseconds", + "75 Microseconds", NULL, }; static const char * const header_mode[] = { @@ -334,7 +343,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) }; static const char * const mpeg4_profile[] = { "Simple", - "Adcanved Simple", + "Advanced Simple", "Core", "Simple Scalable", "Advanced Coding Efficency", @@ -353,6 +362,16 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const jpeg_chroma_subsampling[] = { + "4:4:4", + "4:2:2", + "4:2:0", + "4:1:1", + "4:1:0", + "Gray", + NULL, + }; + switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return mpeg_audio_sampling_freq; @@ -374,6 +393,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_audio_emphasis; case V4L2_CID_MPEG_AUDIO_CRC: return mpeg_audio_crc; + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: + return mpeg_audio_dec_playback; case V4L2_CID_MPEG_VIDEO_ENCODING: return mpeg_video_encoding; case V4L2_CID_MPEG_VIDEO_ASPECT: @@ -414,6 +436,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return mpeg4_profile; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + return jpeg_chroma_subsampling; + default: return NULL; } @@ -492,6 +517,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback"; + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback"; case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; @@ -546,6 +573,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice"; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method"; case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size"; + case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -607,6 +636,14 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FLASH_CHARGE: return "Charge"; case V4L2_CID_FLASH_READY: return "Ready to Strobe"; + /* JPEG encoder controls */ + /* Keep the order of the 'case's the same as in videodev2.h! */ + case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls"; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling"; + case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval"; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; + case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; + default: return NULL; } @@ -674,6 +711,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: case V4L2_CID_MPEG_AUDIO_EMPHASIS: case V4L2_CID_MPEG_AUDIO_CRC: + case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: + case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: case V4L2_CID_MPEG_VIDEO_ENCODING: case V4L2_CID_MPEG_VIDEO_ASPECT: case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: @@ -693,6 +732,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_RDS_TX_PS_NAME: @@ -704,6 +744,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_CLASS: case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: + case V4L2_CID_JPEG_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -717,6 +758,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *max = 0xFFFFFF; break; case V4L2_CID_FLASH_FAULT: + case V4L2_CID_JPEG_ACTIVE_MARKER: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: @@ -724,6 +766,11 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: + case V4L2_CID_MPEG_VIDEO_DEC_PTS: + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1470,7 +1517,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_ctrl); int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *add) { - struct v4l2_ctrl *ctrl; + struct v4l2_ctrl_ref *ref; int ret = 0; /* Do nothing if either handler is NULL or if they are the same */ @@ -1479,7 +1526,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, if (hdl->error) return hdl->error; mutex_lock(&add->lock); - list_for_each_entry(ctrl, &add->ctrls, node) { + list_for_each_entry(ref, &add->ctrl_refs, node) { + struct v4l2_ctrl *ctrl = ref->ctrl; + /* Skip handler-private controls. */ if (ctrl->is_private) continue; @@ -2359,3 +2408,35 @@ void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, v4l2_ctrl_unlock(ctrl); } EXPORT_SYMBOL(v4l2_ctrl_del_event); + +int v4l2_ctrl_log_status(struct file *file, void *fh) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_fh *vfh = file->private_data; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev) + v4l2_ctrl_handler_log_status(vfh->ctrl_handler, + vfd->v4l2_dev->name); + return 0; +} +EXPORT_SYMBOL(v4l2_ctrl_log_status); + +int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type == V4L2_EVENT_CTRL) + return v4l2_event_subscribe(fh, sub, 0); + return -EINVAL; +} +EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); + +unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) +{ + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + return POLLPRI; + poll_wait(file, &fh->wait, wait); + return 0; +} +EXPORT_SYMBOL(v4l2_ctrl_poll); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 96e9615..70bec54 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -26,7 +26,6 @@ #include <linux/kmod.h> #include <linux/slab.h> #include <asm/uaccess.h> -#include <asm/system.h> #include <media/v4l2-common.h> #include <media/v4l2-device.h> @@ -788,7 +787,7 @@ static void __exit videodev_exit(void) unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); } -module_init(videodev_init) +subsys_initcall(videodev_init); module_exit(videodev_exit) MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3f62385..5b2ec1f 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -260,6 +260,8 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", + [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", + [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", @@ -540,10 +542,12 @@ static long __video_do_ioctl(struct file *file, if (!ret) dbgarg(cmd, "driver=%s, card=%s, bus=%s, " "version=0x%08x, " - "capabilities=0x%08x\n", + "capabilities=0x%08x, " + "device_caps=0x%08x\n", cap->driver, cap->card, cap->bus_info, cap->version, - cap->capabilities); + cap->capabilities, + cap->device_caps); break; } @@ -1762,6 +1766,32 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); break; } + case VIDIOC_DECODER_CMD: + { + struct v4l2_decoder_cmd *p = arg; + + if (!ops->vidioc_decoder_cmd) + break; + if (ret_prio) { + ret = ret_prio; + break; + } + ret = ops->vidioc_decoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } + case VIDIOC_TRY_DECODER_CMD: + { + struct v4l2_decoder_cmd *p = arg; + + if (!ops->vidioc_try_decoder_cmd) + break; + ret = ops->vidioc_try_decoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } case VIDIOC_G_PARM: { struct v4l2_streamparm *p = arg; @@ -1909,7 +1939,13 @@ static long __video_do_ioctl(struct file *file, { if (!ops->vidioc_log_status) break; + if (vfd->v4l2_dev) + pr_info("%s: ================= START STATUS =================\n", + vfd->v4l2_dev->name); ret = ops->vidioc_log_status(file, fh); + if (vfd->v4l2_dev) + pr_info("%s: ================== END STATUS ==================\n", + vfd->v4l2_dev->name); break; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2419,7 +2455,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, /* Handles IOCTL */ err = func(file, cmd, parg); if (err == -ENOIOCTLCMD) - err = -EINVAL; + err = -ENOTTY; if (has_array_args) { *kernel_ptr = user_ptr; diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 41d118e..6fe88e9 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -194,8 +194,16 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) } #endif - case VIDIOC_LOG_STATUS: - return v4l2_subdev_call(sd, core, log_status); + case VIDIOC_LOG_STATUS: { + int ret; + + pr_info("%s: ================= START STATUS =================\n", + sd->name); + ret = v4l2_subdev_call(sd, core, log_status); + pr_info("%s: ================== END STATUS ==================\n", + sd->name); + return ret; + } #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) case VIDIOC_SUBDEV_G_FMT: { diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c index 4e789a1..6b5ca6c 100644 --- a/drivers/media/video/videobuf2-vmalloc.c +++ b/drivers/media/video/videobuf2-vmalloc.c @@ -10,6 +10,7 @@ * the Free Software Foundation. */ +#include <linux/io.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/sched.h> @@ -22,6 +23,7 @@ struct vb2_vmalloc_buf { void *vaddr; struct page **pages; + struct vm_area_struct *vma; int write; unsigned long size; unsigned int n_pages; @@ -71,6 +73,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, struct vb2_vmalloc_buf *buf; unsigned long first, last; int n_pages, offset; + struct vm_area_struct *vma; + dma_addr_t physp; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -80,23 +84,37 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, offset = vaddr & ~PAGE_MASK; buf->size = size; - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, vaddr & PAGE_MASK, - buf->n_pages, write, 1, /* force */ - buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; + vma = find_vma(current->mm, vaddr); + if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { + if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) + goto fail_pages_array_alloc; + buf->vma = vma; + buf->vaddr = ioremap_nocache(physp, size); + if (!buf->vaddr) + goto fail_pages_array_alloc; + } else { + first = vaddr >> PAGE_SHIFT; + last = (vaddr + size - 1) >> PAGE_SHIFT; + buf->n_pages = last - first + 1; + buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), + GFP_KERNEL); + if (!buf->pages) + goto fail_pages_array_alloc; + + /* current->mm->mmap_sem is taken by videobuf2 core */ + n_pages = get_user_pages(current, current->mm, + vaddr & PAGE_MASK, buf->n_pages, + write, 1, /* force */ + buf->pages, NULL); + if (n_pages != buf->n_pages) + goto fail_get_user_pages; + + buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + PAGE_KERNEL); + if (!buf->vaddr) + goto fail_get_user_pages; + } buf->vaddr += offset; return buf; @@ -120,14 +138,20 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; unsigned int i; - if (vaddr) - vm_unmap_ram((void *)vaddr, buf->n_pages); - for (i = 0; i < buf->n_pages; ++i) { - if (buf->write) - set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); + if (buf->pages) { + if (vaddr) + vm_unmap_ram((void *)vaddr, buf->n_pages); + for (i = 0; i < buf->n_pages; ++i) { + if (buf->write) + set_page_dirty_lock(buf->pages[i]); + put_page(buf->pages[i]); + } + kfree(buf->pages); + } else { + if (buf->vma) + vb2_put_vma(buf->vma); + iounmap(buf->vaddr); } - kfree(buf->pages); kfree(buf); } diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 7d754fb..5e8b071 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -819,8 +819,9 @@ static int vidioc_querycap(struct file *file, void *priv, strcpy(cap->driver, "vivi"); strcpy(cap->card, "vivi"); strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \ + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -958,14 +959,6 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return vb2_streamoff(&dev->vb_vidq, i); } -static int vidioc_log_status(struct file *file, void *priv) -{ - struct vivi_dev *dev = video_drvdata(file); - - v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name); - return 0; -} - static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) { return 0; @@ -1008,17 +1001,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int vidioc_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); - default: - return -EINVAL; - } -} - /* --- controls ---------------------------------------------- */ static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -1209,8 +1191,8 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index c15efb6..7cfbc9d 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -208,15 +208,4 @@ static struct i2c_driver vp27smpx_driver = { .id_table = vp27smpx_id, }; -static __init int init_vp27smpx(void) -{ - return i2c_add_driver(&vp27smpx_driver); -} - -static __exit void exit_vp27smpx(void) -{ - i2c_del_driver(&vp27smpx_driver); -} - -module_init(init_vp27smpx); -module_exit(exit_vp27smpx); +module_i2c_driver(vp27smpx_driver); diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index e5cad6f..2f67b4c 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -588,15 +588,4 @@ static struct i2c_driver vpx3220_driver = { .id_table = vpx3220_id, }; -static __init int init_vpx3220(void) -{ - return i2c_add_driver(&vpx3220_driver); -} - -static __exit void exit_vpx3220(void) -{ - i2c_del_driver(&vpx3220_driver); -} - -module_init(init_vpx3220); -module_exit(exit_vpx3220); +module_i2c_driver(vpx3220_driver); diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c new file mode 100644 index 0000000..42ae9dc --- /dev/null +++ b/drivers/media/video/vs6624.c @@ -0,0 +1,928 @@ +/* + * vs6624.c ST VS6624 CMOS image sensor driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> + +#include "vs6624_regs.h" + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 +#define QQCIF_WIDTH 88 +#define QQCIF_HEIGHT 72 + +#define MAX_FRAME_RATE 30 + +struct vs6624 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct v4l2_fract frame_rate; + struct v4l2_mbus_framefmt fmt; + unsigned ce_pin; +}; + +static const struct vs6624_format { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; +} vs6624_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + { + .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + }, +}; + +static struct v4l2_mbus_framefmt vs6624_default_fmt = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .code = V4L2_MBUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, +}; + +static const u16 vs6624_p1[] = { + 0x8104, 0x03, + 0x8105, 0x01, + 0xc900, 0x03, + 0xc904, 0x47, + 0xc905, 0x10, + 0xc906, 0x80, + 0xc907, 0x3a, + 0x903a, 0x02, + 0x903b, 0x47, + 0x903c, 0x15, + 0xc908, 0x31, + 0xc909, 0xdc, + 0xc90a, 0x80, + 0xc90b, 0x44, + 0x9044, 0x02, + 0x9045, 0x31, + 0x9046, 0xe2, + 0xc90c, 0x07, + 0xc90d, 0xe0, + 0xc90e, 0x80, + 0xc90f, 0x47, + 0x9047, 0x90, + 0x9048, 0x83, + 0x9049, 0x81, + 0x904a, 0xe0, + 0x904b, 0x60, + 0x904c, 0x08, + 0x904d, 0x90, + 0x904e, 0xc0, + 0x904f, 0x43, + 0x9050, 0x74, + 0x9051, 0x01, + 0x9052, 0xf0, + 0x9053, 0x80, + 0x9054, 0x05, + 0x9055, 0xE4, + 0x9056, 0x90, + 0x9057, 0xc0, + 0x9058, 0x43, + 0x9059, 0xf0, + 0x905a, 0x02, + 0x905b, 0x07, + 0x905c, 0xec, + 0xc910, 0x5d, + 0xc911, 0xca, + 0xc912, 0x80, + 0xc913, 0x5d, + 0x905d, 0xa3, + 0x905e, 0x04, + 0x905f, 0xf0, + 0x9060, 0xa3, + 0x9061, 0x04, + 0x9062, 0xf0, + 0x9063, 0x22, + 0xc914, 0x72, + 0xc915, 0x92, + 0xc916, 0x80, + 0xc917, 0x64, + 0x9064, 0x74, + 0x9065, 0x01, + 0x9066, 0x02, + 0x9067, 0x72, + 0x9068, 0x95, + 0xc918, 0x47, + 0xc919, 0xf2, + 0xc91a, 0x81, + 0xc91b, 0x69, + 0x9169, 0x74, + 0x916a, 0x02, + 0x916b, 0xf0, + 0x916c, 0xec, + 0x916d, 0xb4, + 0x916e, 0x10, + 0x916f, 0x0a, + 0x9170, 0x90, + 0x9171, 0x80, + 0x9172, 0x16, + 0x9173, 0xe0, + 0x9174, 0x70, + 0x9175, 0x04, + 0x9176, 0x90, + 0x9177, 0xd3, + 0x9178, 0xc4, + 0x9179, 0xf0, + 0x917a, 0x22, + 0xc91c, 0x0a, + 0xc91d, 0xbe, + 0xc91e, 0x80, + 0xc91f, 0x73, + 0x9073, 0xfc, + 0x9074, 0xa3, + 0x9075, 0xe0, + 0x9076, 0xf5, + 0x9077, 0x82, + 0x9078, 0x8c, + 0x9079, 0x83, + 0x907a, 0xa3, + 0x907b, 0xa3, + 0x907c, 0xe0, + 0x907d, 0xfc, + 0x907e, 0xa3, + 0x907f, 0xe0, + 0x9080, 0xc3, + 0x9081, 0x9f, + 0x9082, 0xff, + 0x9083, 0xec, + 0x9084, 0x9e, + 0x9085, 0xfe, + 0x9086, 0x02, + 0x9087, 0x0a, + 0x9088, 0xea, + 0xc920, 0x47, + 0xc921, 0x38, + 0xc922, 0x80, + 0xc923, 0x89, + 0x9089, 0xec, + 0x908a, 0xd3, + 0x908b, 0x94, + 0x908c, 0x20, + 0x908d, 0x40, + 0x908e, 0x01, + 0x908f, 0x1c, + 0x9090, 0x90, + 0x9091, 0xd3, + 0x9092, 0xd4, + 0x9093, 0xec, + 0x9094, 0xf0, + 0x9095, 0x02, + 0x9096, 0x47, + 0x9097, 0x3d, + 0xc924, 0x45, + 0xc925, 0xca, + 0xc926, 0x80, + 0xc927, 0x98, + 0x9098, 0x12, + 0x9099, 0x77, + 0x909a, 0xd6, + 0x909b, 0x02, + 0x909c, 0x45, + 0x909d, 0xcd, + 0xc928, 0x20, + 0xc929, 0xd5, + 0xc92a, 0x80, + 0xc92b, 0x9e, + 0x909e, 0x90, + 0x909f, 0x82, + 0x90a0, 0x18, + 0x90a1, 0xe0, + 0x90a2, 0xb4, + 0x90a3, 0x03, + 0x90a4, 0x0e, + 0x90a5, 0x90, + 0x90a6, 0x83, + 0x90a7, 0xbf, + 0x90a8, 0xe0, + 0x90a9, 0x60, + 0x90aa, 0x08, + 0x90ab, 0x90, + 0x90ac, 0x81, + 0x90ad, 0xfc, + 0x90ae, 0xe0, + 0x90af, 0xff, + 0x90b0, 0xc3, + 0x90b1, 0x13, + 0x90b2, 0xf0, + 0x90b3, 0x90, + 0x90b4, 0x81, + 0x90b5, 0xfc, + 0x90b6, 0xe0, + 0x90b7, 0xff, + 0x90b8, 0x02, + 0x90b9, 0x20, + 0x90ba, 0xda, + 0xc92c, 0x70, + 0xc92d, 0xbc, + 0xc92e, 0x80, + 0xc92f, 0xbb, + 0x90bb, 0x90, + 0x90bc, 0x82, + 0x90bd, 0x18, + 0x90be, 0xe0, + 0x90bf, 0xb4, + 0x90c0, 0x03, + 0x90c1, 0x06, + 0x90c2, 0x90, + 0x90c3, 0xc1, + 0x90c4, 0x06, + 0x90c5, 0x74, + 0x90c6, 0x05, + 0x90c7, 0xf0, + 0x90c8, 0x90, + 0x90c9, 0xd3, + 0x90ca, 0xa0, + 0x90cb, 0x02, + 0x90cc, 0x70, + 0x90cd, 0xbf, + 0xc930, 0x72, + 0xc931, 0x21, + 0xc932, 0x81, + 0xc933, 0x3b, + 0x913b, 0x7d, + 0x913c, 0x02, + 0x913d, 0x7f, + 0x913e, 0x7b, + 0x913f, 0x02, + 0x9140, 0x72, + 0x9141, 0x25, + 0xc934, 0x28, + 0xc935, 0xae, + 0xc936, 0x80, + 0xc937, 0xd2, + 0x90d2, 0xf0, + 0x90d3, 0x90, + 0x90d4, 0xd2, + 0x90d5, 0x0a, + 0x90d6, 0x02, + 0x90d7, 0x28, + 0x90d8, 0xb4, + 0xc938, 0x28, + 0xc939, 0xb1, + 0xc93a, 0x80, + 0xc93b, 0xd9, + 0x90d9, 0x90, + 0x90da, 0x83, + 0x90db, 0xba, + 0x90dc, 0xe0, + 0x90dd, 0xff, + 0x90de, 0x90, + 0x90df, 0xd2, + 0x90e0, 0x08, + 0x90e1, 0xe0, + 0x90e2, 0xe4, + 0x90e3, 0xef, + 0x90e4, 0xf0, + 0x90e5, 0xa3, + 0x90e6, 0xe0, + 0x90e7, 0x74, + 0x90e8, 0xff, + 0x90e9, 0xf0, + 0x90ea, 0x90, + 0x90eb, 0xd2, + 0x90ec, 0x0a, + 0x90ed, 0x02, + 0x90ee, 0x28, + 0x90ef, 0xb4, + 0xc93c, 0x29, + 0xc93d, 0x79, + 0xc93e, 0x80, + 0xc93f, 0xf0, + 0x90f0, 0xf0, + 0x90f1, 0x90, + 0x90f2, 0xd2, + 0x90f3, 0x0e, + 0x90f4, 0x02, + 0x90f5, 0x29, + 0x90f6, 0x7f, + 0xc940, 0x29, + 0xc941, 0x7c, + 0xc942, 0x80, + 0xc943, 0xf7, + 0x90f7, 0x90, + 0x90f8, 0x83, + 0x90f9, 0xba, + 0x90fa, 0xe0, + 0x90fb, 0xff, + 0x90fc, 0x90, + 0x90fd, 0xd2, + 0x90fe, 0x0c, + 0x90ff, 0xe0, + 0x9100, 0xe4, + 0x9101, 0xef, + 0x9102, 0xf0, + 0x9103, 0xa3, + 0x9104, 0xe0, + 0x9105, 0x74, + 0x9106, 0xff, + 0x9107, 0xf0, + 0x9108, 0x90, + 0x9109, 0xd2, + 0x910a, 0x0e, + 0x910b, 0x02, + 0x910c, 0x29, + 0x910d, 0x7f, + 0xc944, 0x2a, + 0xc945, 0x42, + 0xc946, 0x81, + 0xc947, 0x0e, + 0x910e, 0xf0, + 0x910f, 0x90, + 0x9110, 0xd2, + 0x9111, 0x12, + 0x9112, 0x02, + 0x9113, 0x2a, + 0x9114, 0x48, + 0xc948, 0x2a, + 0xc949, 0x45, + 0xc94a, 0x81, + 0xc94b, 0x15, + 0x9115, 0x90, + 0x9116, 0x83, + 0x9117, 0xba, + 0x9118, 0xe0, + 0x9119, 0xff, + 0x911a, 0x90, + 0x911b, 0xd2, + 0x911c, 0x10, + 0x911d, 0xe0, + 0x911e, 0xe4, + 0x911f, 0xef, + 0x9120, 0xf0, + 0x9121, 0xa3, + 0x9122, 0xe0, + 0x9123, 0x74, + 0x9124, 0xff, + 0x9125, 0xf0, + 0x9126, 0x90, + 0x9127, 0xd2, + 0x9128, 0x12, + 0x9129, 0x02, + 0x912a, 0x2a, + 0x912b, 0x48, + 0xc900, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_p2[] = { + 0x806f, 0x01, + 0x058c, 0x01, + 0x0000, 0x00, +}; + +static const u16 vs6624_run_setup[] = { + 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ + VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ + VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ + VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ + VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ + VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ + VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ + VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ + VS6624_NORA_USAGE, 0x04, /* Nora usage */ + VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ + VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ + VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ + VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ + VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ + VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ + VS6624_F2B_DISABLE, 0x00, /* Disable */ + 0x1d8a, 0x30, /* MAXWeightHigh */ + 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ + 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ + 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ + 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ + 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ + 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ + 0x1e08, 0x06, /* MAXWeightLow */ + 0x1e0a, 0x0a, /* MAXWeightHigh */ + 0x1601, 0x3a, /* Red A MSB */ + 0x1602, 0x14, /* Red A LSB */ + 0x1605, 0x3b, /* Blue A MSB */ + 0x1606, 0x85, /* BLue A LSB */ + 0x1609, 0x3b, /* RED B MSB */ + 0x160a, 0x85, /* RED B LSB */ + 0x160d, 0x3a, /* Blue B MSB */ + 0x160e, 0x14, /* Blue B LSB */ + 0x1611, 0x30, /* Max Distance from Locus MSB */ + 0x1612, 0x8f, /* Max Distance from Locus MSB */ + 0x1614, 0x01, /* Enable constrainer */ + 0x0000, 0x00, +}; + +static const u16 vs6624_default[] = { + VS6624_CONTRAST0, 0x84, + VS6624_SATURATION0, 0x75, + VS6624_GAMMA0, 0x11, + VS6624_CONTRAST1, 0x84, + VS6624_SATURATION1, 0x75, + VS6624_GAMMA1, 0x11, + VS6624_MAN_RG, 0x80, + VS6624_MAN_GG, 0x80, + VS6624_MAN_BG, 0x80, + VS6624_WB_MODE, 0x1, + VS6624_EXPO_COMPENSATION, 0xfe, + VS6624_EXPO_METER, 0x0, + VS6624_LIGHT_FREQ, 0x64, + VS6624_PEAK_GAIN, 0xe, + VS6624_PEAK_LOW_THR, 0x28, + VS6624_HMIRROR0, 0x0, + VS6624_VFLIP0, 0x0, + VS6624_ZOOM_HSTEP0_MSB, 0x0, + VS6624_ZOOM_HSTEP0_LSB, 0x1, + VS6624_ZOOM_VSTEP0_MSB, 0x0, + VS6624_ZOOM_VSTEP0_LSB, 0x1, + VS6624_PAN_HSTEP0_MSB, 0x0, + VS6624_PAN_HSTEP0_LSB, 0xf, + VS6624_PAN_VSTEP0_MSB, 0x0, + VS6624_PAN_VSTEP0_LSB, 0xf, + VS6624_SENSOR_MODE, 0x1, + VS6624_SYNC_CODE_SETUP, 0x21, + VS6624_DISABLE_FR_DAMPER, 0x0, + VS6624_FR_DEN, 0x1, + VS6624_FR_NUM_LSB, 0xf, + VS6624_INIT_PIPE_SETUP, 0x0, + VS6624_IMG_FMT0, 0x0, + VS6624_YUV_SETUP, 0x1, + VS6624_IMAGE_SIZE0, 0x2, + 0x0000, 0x00, +}; + +static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vs6624, sd); +} +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vs6624, hdl)->sd; +} + +static int vs6624_read(struct v4l2_subdev *sd, u16 index) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + + buf[0] = index >> 8; + buf[1] = index; + i2c_master_send(client, buf, 2); + i2c_master_recv(client, buf, 1); + + return buf[0]; +} + +static int vs6624_write(struct v4l2_subdev *sd, u16 index, + u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[3]; + + buf[0] = index >> 8; + buf[1] = index; + buf[2] = value; + + return i2c_master_send(client, buf, 3); +} + +static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) +{ + u16 reg; + u8 data; + + while (*regs != 0x00) { + reg = *regs++; + data = *regs++; + + vs6624_write(sd, reg, data); + } + return 0; +} + +static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); + break; + case V4L2_CID_SATURATION: + vs6624_write(sd, VS6624_SATURATION0, ctrl->val); + break; + case V4L2_CID_HFLIP: + vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); + break; + case V4L2_CID_VFLIP: + vs6624_write(sd, VS6624_VFLIP0, ctrl->val); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(vs6624_formats)) + return -EINVAL; + + *code = vs6624_formats[index].mbus_code; + return 0; +} + +static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) + if (vs6624_formats[index].mbus_code == fmt->code) + break; + if (index >= ARRAY_SIZE(vs6624_formats)) { + /* default to first format */ + index = 0; + fmt->code = vs6624_formats[0].mbus_code; + } + + /* sensor mode is VGA */ + if (fmt->width > VGA_WIDTH) + fmt->width = VGA_WIDTH; + if (fmt->height > VGA_HEIGHT) + fmt->height = VGA_HEIGHT; + fmt->width = fmt->width & (~3); + fmt->height = fmt->height & (~3); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = vs6624_formats[index].colorspace; + return 0; +} + +static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + int ret; + + ret = vs6624_try_mbus_fmt(sd, fmt); + if (ret) + return ret; + + /* set image format */ + switch (fmt->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x1); + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + vs6624_write(sd, VS6624_IMG_FMT0, 0x0); + vs6624_write(sd, VS6624_YUV_SETUP, 0x3); + break; + case V4L2_MBUS_FMT_RGB565_2X8_LE: + vs6624_write(sd, VS6624_IMG_FMT0, 0x4); + vs6624_write(sd, VS6624_RGB_SETUP, 0x0); + break; + default: + return -EINVAL; + } + + /* set image size */ + if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); + else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); + else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); + else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); + else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); + else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); + else { + vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); + vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); + vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); + vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); + vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); + vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); + } + + sensor->fmt = *fmt; + + return 0; +} + +static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vs6624 *sensor = to_vs6624(sd); + + *fmt = sensor->fmt; + return 0; +} + +static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(*cp)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = sensor->frame_rate.denominator; + cp->timeperframe.denominator = sensor->frame_rate.numerator; + return 0; +} + +static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct vs6624 *sensor = to_vs6624(sd); + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + if (tpf->numerator == 0 || tpf->denominator == 0 + || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { + /* reset to max frame rate */ + tpf->numerator = 1; + tpf->denominator = MAX_FRAME_RATE; + } + sensor->frame_rate.numerator = tpf->denominator; + sensor->frame_rate.denominator = tpf->numerator; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + return 0; +} + +static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) +{ + if (enable) + vs6624_write(sd, VS6624_USER_CMD, 0x2); + else + vs6624_write(sd, VS6624_USER_CMD, 0x4); + udelay(100); + return 0; +} + +static int vs6624_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) + | vs6624_read(sd, VS6624_FW_VSN_MINOR); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = vs6624_read(sd, reg->reg & 0xffff); + reg->size = 1; + return 0; +} + +static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { + .s_ctrl = vs6624_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops vs6624_core_ops = { + .g_chip_ident = vs6624_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = vs6624_g_register, + .s_register = vs6624_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops vs6624_video_ops = { + .enum_mbus_fmt = vs6624_enum_mbus_fmt, + .try_mbus_fmt = vs6624_try_mbus_fmt, + .s_mbus_fmt = vs6624_s_mbus_fmt, + .g_mbus_fmt = vs6624_g_mbus_fmt, + .s_parm = vs6624_s_parm, + .g_parm = vs6624_g_parm, + .s_stream = vs6624_s_stream, +}; + +static const struct v4l2_subdev_ops vs6624_ops = { + .core = &vs6624_core_ops, + .video = &vs6624_video_ops, +}; + +static int __devinit vs6624_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vs6624 *sensor; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + const unsigned *ce; + int ret; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EIO; + + ce = client->dev.platform_data; + if (ce == NULL) + return -EINVAL; + + ret = gpio_request(*ce, "VS6624 Chip Enable"); + if (ret) { + v4l_err(client, "failed to request GPIO %d\n", *ce); + return ret; + } + gpio_direction_output(*ce, 1); + /* wait 100ms before any further i2c writes are performed */ + mdelay(100); + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) { + gpio_free(*ce); + return -ENOMEM; + } + + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &vs6624_ops); + + vs6624_writeregs(sd, vs6624_p1); + vs6624_write(sd, VS6624_MICRO_EN, 0x2); + vs6624_write(sd, VS6624_DIO_EN, 0x1); + mdelay(10); + vs6624_writeregs(sd, vs6624_p2); + + vs6624_writeregs(sd, vs6624_default); + vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); + vs6624_writeregs(sd, vs6624_run_setup); + + /* set frame rate */ + sensor->frame_rate.numerator = MAX_FRAME_RATE; + sensor->frame_rate.denominator = 1; + vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); + vs6624_write(sd, VS6624_FR_NUM_MSB, + sensor->frame_rate.numerator >> 8); + vs6624_write(sd, VS6624_FR_NUM_LSB, + sensor->frame_rate.numerator & 0xFF); + vs6624_write(sd, VS6624_FR_DEN, + sensor->frame_rate.denominator & 0xFF); + + sensor->fmt = vs6624_default_fmt; + sensor->ce_pin = *ce; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + hdl = &sensor->hdl; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* hook the control handler into the driver */ + sd->ctrl_handler = hdl; + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + return err; + } + + /* initialize the hardware to the default control values */ + ret = v4l2_ctrl_handler_setup(hdl); + if (ret) { + v4l2_ctrl_handler_free(hdl); + kfree(sensor); + gpio_free(*ce); + } + return ret; +} + +static int __devexit vs6624_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vs6624 *sensor = to_vs6624(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + gpio_free(sensor->ce_pin); + kfree(sensor); + return 0; +} + +static const struct i2c_device_id vs6624_id[] = { + {"vs6624", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, vs6624_id); + +static struct i2c_driver vs6624_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "vs6624", + }, + .probe = vs6624_probe, + .remove = __devexit_p(vs6624_remove), + .id_table = vs6624_id, +}; + +static __init int vs6624_init(void) +{ + return i2c_add_driver(&vs6624_driver); +} + +static __exit void vs6624_exit(void) +{ + i2c_del_driver(&vs6624_driver); +} + +module_init(vs6624_init); +module_exit(vs6624_exit); + +MODULE_DESCRIPTION("VS6624 sensor driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h new file mode 100644 index 0000000..6ba2ee2 --- /dev/null +++ b/drivers/media/video/vs6624_regs.h @@ -0,0 +1,337 @@ +/* + * vs6624 - ST VS6624 CMOS image sensor registers + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _VS6624_REGS_H_ +#define _VS6624_REGS_H_ + +/* low level control registers */ +#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ +#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ +/* device parameters */ +#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ +#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ +#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ +#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ +#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ +#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ +/* host interface manager control */ +#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ +/* host interface manager status */ +#define VS6624_STATE 0x0202 /* current state of the mode manager */ +/* run mode control */ +#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ +/* mode setup */ +#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ +#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ +/* pipe setup bank0 */ +#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ +#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ +#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ +#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ +#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ +#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ +#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ +#define VS6624_GAMMA0 0x03B8 /* gamma settings */ +#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ +#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ +/* pipe setup bank1 */ +#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ +#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ +#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ +#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ +#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ +#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ +#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ +#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ +#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ +#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ +#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ +#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ +#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ +#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ +#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ +#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ +#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ +#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ +#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ +#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ +#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ +#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ +#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ +#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ +#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ +#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ +#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ +#define VS6624_SATURATION1 0x0436 /* saturation control for output */ +#define VS6624_GAMMA1 0x0438 /* gamma settings */ +#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ +#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ +#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ +/* view live control */ +#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ +#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ +/* view live status */ +#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ +/* power management */ +#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ +/* video timing parameter host inputs */ +#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ +#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ +#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ +/* video timing control */ +#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ +/* frame dimension parameter host inputs */ +#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ +#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ +/* static frame rate control */ +#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ +#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ +#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ +/* automatic frame rate control */ +#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ +#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ +#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ +/* exposure controls */ +#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ +#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ +#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ +#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ +#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ +#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ +#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ +#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ +#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ +#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ +#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ +#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ +#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ +#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ +#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ +#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ +#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ +#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ +#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ +#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ +#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ +#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ +#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ +#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ +#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ +#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ +#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ +#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ +/* white balance control */ +#define VS6624_WB_MODE 0x1480 /* set white balance mode */ +#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ +#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ +#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ +#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ +#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ +#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ +#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ +#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ +#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ +/* sensor setup */ +#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ +/* image stability */ +#define VS6624_STABLE_WB 0x1900 /* white balance stable */ +#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ +#define VS6624_STABLE 0x1906 /* system stable */ +/* flash control */ +#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ +#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ +#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ +/* flash status */ +#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ +#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ +/* scythe filter controls */ +#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ +/* jack filter controls */ +#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ +/* demosaic control */ +#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ +/* color matrix dampers */ +#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ +#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ +#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ +#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ +#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ +#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ +#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ +/* peaking control */ +#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ +#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ +#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ +#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ +#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ +#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ +#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ +#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ +#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ +#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ +#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ +#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ +#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ +#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ +#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ +#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ +#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ +/* pipe 0 RGB to YUV matrix manual control */ +#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ +#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ +#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ +#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ +#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ +#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ +/* pipe 1 RGB to YUV matrix manual control */ +#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ +#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ +#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ +#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ +#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ +#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ +#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ +#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ +#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ +#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ +#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ +#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ +#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ +/* pipe 0 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ +/* pipe 1 gamma manual control */ +#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ +#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ +#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ +#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ +#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ +#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ +#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ +/* fade to black */ +#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ +#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ +#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ +#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ +#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ +#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ +#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ +#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ +#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ +/* output formatter control */ +#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ +#define VS6624_BLANK_FMT 0x2582 /* blank format */ +#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ +#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ +#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ +#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ +#define VS6624_PCLK_EN 0x258C /* PCLK enable */ +#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ +#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ +#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ +#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ +#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ +#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ +#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ +#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ +#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ +#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ +#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ +#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ +#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ +#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ +#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ +#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ +#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ +#define VS6624_OUT_IF 0x25B0 /* output interface */ +#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ +/* NoRA controls */ +#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ +#define VS6624_NORA_USAGE 0x2602 /* usage */ +#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ +#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ +#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ +#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ +#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ +#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ +#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ +#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ +#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ +#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ + +#endif diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 453dbbd..7fd7ac5 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -129,9 +129,9 @@ MODULE_LICENSE("GPL"); MODULE_VERSION("0.33.1"); #ifdef MODULE -static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; +static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; #else -static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; +static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; #endif module_param_array(pardev, charp, NULL, 0); MODULE_PARM_DESC(pardev, "pardev: where to search for\n" diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index a22f765..3bb99e9 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -291,15 +291,4 @@ static struct i2c_driver wm8739_driver = { .id_table = wm8739_id, }; -static __init int init_wm8739(void) -{ - return i2c_add_driver(&wm8739_driver); -} - -static __exit void exit_wm8739(void) -{ - i2c_del_driver(&wm8739_driver); -} - -module_init(init_wm8739); -module_exit(exit_wm8739); +module_i2c_driver(wm8739_driver); diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 9cedb1e..bee77ea 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -339,15 +339,4 @@ static struct i2c_driver wm8775_driver = { .id_table = wm8775_id, }; -static __init int init_wm8775(void) -{ - return i2c_add_driver(&wm8775_driver); -} - -static __exit void exit_wm8775(void) -{ - i2c_del_driver(&wm8775_driver); -} - -module_init(init_wm8775); -module_exit(exit_wm8775); +module_i2c_driver(wm8775_driver); |